From: Linus Torvalds Date: Wed, 4 Oct 2006 15:15:55 +0000 (-0700) Subject: Merge branch 'audit.b32' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit... X-Git-Tag: v2.6.19-rc1~36 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18e6756a6b463e09fd3873592ec6b0579c78103d;hp=c8e649ba908954447e9a095677f6a6c8e50a37b2;p=linux-2.6 Merge branch 'audit.b32' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current * 'audit.b32' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current: [PATCH] message types updated [PATCH] name_count array overrun [PATCH] PPID filtering fix [PATCH] arch filter lists with < or > should not be accepted --- diff --git a/CREDITS b/CREDITS index dba3e63346..5d75254bcb 100644 --- a/CREDITS +++ b/CREDITS @@ -2240,6 +2240,12 @@ D: tc: HFSC scheduler S: Freiburg S: Germany +N: Paul E. McKenney +E: paulmck@us.ibm.com +W: http://www.rdrop.com/users/paulmck/ +D: RCU and variants +D: rcutorture module + N: Mike McLagan E: mike.mclagan@linux.org W: http://www.invlogic.com/~mmclagan @@ -2981,6 +2987,10 @@ S: 69 rue Dunois S: 75013 Paris S: France +N: Dipankar Sarma +E: dipankar@in.ibm.com +D: RCU + N: Hannu Savolainen E: hannu@opensound.com D: Maintainer of the sound drivers until 2.1.x days. @@ -3293,6 +3303,12 @@ S: 3 Ballow Crescent S: MacGregor A.C.T 2615 S: Australia +N: Josh Triplett +E: josh@freedesktop.org +P: 1024D/D0FE7AFB B24A 65C9 1D71 2AC2 DE87 CA26 189B 9946 D0FE 7AFB +D: rcutorture maintainer +D: lock annotations, finding and fixing lock bugs + N: Winfried Trümper E: winni@xpilot.org W: http://www.shop.de/~winni/ diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 49c745720f..2b5ac60494 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -158,6 +158,7 @@ X!Ilib/string.c !Emm/filemap.c !Emm/memory.c !Emm/vmalloc.c +!Imm/page_alloc.c !Emm/mempool.c !Emm/page-writeback.c !Emm/truncate.c diff --git a/Documentation/DocBook/libata.tmpl b/Documentation/DocBook/libata.tmpl index c684abf0d3..07a635590b 100644 --- a/Documentation/DocBook/libata.tmpl +++ b/Documentation/DocBook/libata.tmpl @@ -14,7 +14,7 @@ - 2003-2005 + 2003-2006 Jeff Garzik diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index 1d50cf0c90..f4dffadbcb 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -221,3 +221,41 @@ over a rather long period of time, but improvements are always welcome! disable irq on a given acquisition of that lock will result in deadlock as soon as the RCU callback happens to interrupt that acquisition's critical section. + +13. SRCU (srcu_read_lock(), srcu_read_unlock(), and synchronize_srcu()) + may only be invoked from process context. Unlike other forms of + RCU, it -is- permissible to block in an SRCU read-side critical + section (demarked by srcu_read_lock() and srcu_read_unlock()), + hence the "SRCU": "sleepable RCU". Please note that if you + don't need to sleep in read-side critical sections, you should + be using RCU rather than SRCU, because RCU is almost always + faster and easier to use than is SRCU. + + Also unlike other forms of RCU, explicit initialization + and cleanup is required via init_srcu_struct() and + cleanup_srcu_struct(). These are passed a "struct srcu_struct" + that defines the scope of a given SRCU domain. Once initialized, + the srcu_struct is passed to srcu_read_lock(), srcu_read_unlock() + and synchronize_srcu(). A given synchronize_srcu() waits only + for SRCU read-side critical sections governed by srcu_read_lock() + and srcu_read_unlock() calls that have been passd the same + srcu_struct. This property is what makes sleeping read-side + critical sections tolerable -- a given subsystem delays only + its own updates, not those of other subsystems using SRCU. + Therefore, SRCU is less prone to OOM the system than RCU would + be if RCU's read-side critical sections were permitted to + sleep. + + The ability to sleep in read-side critical sections does not + come for free. First, corresponding srcu_read_lock() and + srcu_read_unlock() calls must be passed the same srcu_struct. + Second, grace-period-detection overhead is amortized only + over those updates sharing a given srcu_struct, rather than + being globally amortized as they are for other forms of RCU. + Therefore, SRCU should be used in preference to rw_semaphore + only in extremely read-intensive situations, or in situations + requiring SRCU's read-side deadlock immunity or low read-side + realtime latency. + + Note that, rcu_assign_pointer() and rcu_dereference() relate to + SRCU just as they do to other forms of RCU. diff --git a/Documentation/RCU/rcu.txt b/Documentation/RCU/rcu.txt index 02e27bf1d3..f84407cba8 100644 --- a/Documentation/RCU/rcu.txt +++ b/Documentation/RCU/rcu.txt @@ -45,7 +45,8 @@ o How can I see where RCU is currently used in the Linux kernel? Search for "rcu_read_lock", "rcu_read_unlock", "call_rcu", "rcu_read_lock_bh", "rcu_read_unlock_bh", "call_rcu_bh", - "synchronize_rcu", and "synchronize_net". + "srcu_read_lock", "srcu_read_unlock", "synchronize_rcu", + "synchronize_net", and "synchronize_srcu". o What guidelines should I follow when writing code that uses RCU? diff --git a/Documentation/RCU/torture.txt b/Documentation/RCU/torture.txt index a494859160..25a3c3f7d3 100644 --- a/Documentation/RCU/torture.txt +++ b/Documentation/RCU/torture.txt @@ -28,6 +28,15 @@ nreaders This is the number of RCU reading threads supported. To properly exercise RCU implementations with preemptible read-side critical sections. +nfakewriters This is the number of RCU fake writer threads to run. Fake + writer threads repeatedly use the synchronous "wait for + current readers" function of the interface selected by + torture_type, with a delay between calls to allow for various + different numbers of writers running in parallel. + nfakewriters defaults to 4, which provides enough parallelism + to trigger special cases caused by multiple writers, such as + the synchronize_srcu() early return optimization. + stat_interval The number of seconds between output of torture statistics (via printk()). Regardless of the interval, statistics are printed when the module is unloaded. @@ -44,9 +53,12 @@ test_no_idle_hz Whether or not to test the ability of RCU to operate in a kernel that disables the scheduling-clock interrupt to idle CPUs. Boolean parameter, "1" to test, "0" otherwise. -torture_type The type of RCU to test: "rcu" for the rcu_read_lock() - API, "rcu_bh" for the rcu_read_lock_bh() API, and "srcu" - for the "srcu_read_lock()" API. +torture_type The type of RCU to test: "rcu" for the rcu_read_lock() API, + "rcu_sync" for rcu_read_lock() with synchronous reclamation, + "rcu_bh" for the rcu_read_lock_bh() API, "rcu_bh_sync" for + rcu_read_lock_bh() with synchronous reclamation, "srcu" for + the "srcu_read_lock()" API, and "sched" for the use of + preempt_disable() together with synchronize_sched(). verbose Enable debug printk()s. Default is disabled. @@ -118,6 +130,21 @@ o "Free-Block Circulation": Shows the number of torture structures as it is only incremented if a torture structure's counter somehow gets incremented farther than it should. +Different implementations of RCU can provide implementation-specific +additional information. For example, SRCU provides the following: + + srcu-torture: rtc: f8cf46a8 ver: 355 tfle: 0 rta: 356 rtaf: 0 rtf: 346 rtmbe: 0 + srcu-torture: Reader Pipe: 559738 939 0 0 0 0 0 0 0 0 0 + srcu-torture: Reader Batch: 560434 243 0 0 0 0 0 0 0 0 + srcu-torture: Free-Block Circulation: 355 354 353 352 351 350 349 348 347 346 0 + srcu-torture: per-CPU(idx=1): 0(0,1) 1(0,1) 2(0,0) 3(0,1) + +The first four lines are similar to those for RCU. The last line shows +the per-CPU counter state. The numbers in parentheses are the values +of the "old" and "current" counters for the corresponding CPU. The +"idx" value maps the "old" and "current" values to the underlying array, +and is useful for debugging. + USAGE diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index 820fee2369..e0d6d99b8f 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -778,6 +778,8 @@ Markers for RCU read-side critical sections: rcu_read_unlock rcu_read_lock_bh rcu_read_unlock_bh + srcu_read_lock + srcu_read_unlock RCU pointer/list traversal: @@ -804,6 +806,7 @@ RCU grace period: synchronize_net synchronize_sched synchronize_rcu + synchronize_srcu call_rcu call_rcu_bh diff --git a/Documentation/ecryptfs.txt b/Documentation/ecryptfs.txt new file mode 100644 index 0000000000..01d8a08351 --- /dev/null +++ b/Documentation/ecryptfs.txt @@ -0,0 +1,77 @@ +eCryptfs: A stacked cryptographic filesystem for Linux + +eCryptfs is free software. Please see the file COPYING for details. +For documentation, please see the files in the doc/ subdirectory. For +building and installation instructions please see the INSTALL file. + +Maintainer: Phillip Hellewell +Lead developer: Michael A. Halcrow +Developers: Michael C. Thompson + Kent Yoder +Web Site: http://ecryptfs.sf.net + +This software is currently undergoing development. Make sure to +maintain a backup copy of any data you write into eCryptfs. + +eCryptfs requires the userspace tools downloadable from the +SourceForge site: + +http://sourceforge.net/projects/ecryptfs/ + +Userspace requirements include: + - David Howells' userspace keyring headers and libraries (version + 1.0 or higher), obtainable from + http://people.redhat.com/~dhowells/keyutils/ + - Libgcrypt + + +NOTES + +In the beta/experimental releases of eCryptfs, when you upgrade +eCryptfs, you should copy the files to an unencrypted location and +then copy the files back into the new eCryptfs mount to migrate the +files. + + +MOUNT-WIDE PASSPHRASE + +Create a new directory into which eCryptfs will write its encrypted +files (i.e., /root/crypt). Then, create the mount point directory +(i.e., /mnt/crypt). Now it's time to mount eCryptfs: + +mount -t ecryptfs /root/crypt /mnt/crypt + +You should be prompted for a passphrase and a salt (the salt may be +blank). + +Try writing a new file: + +echo "Hello, World" > /mnt/crypt/hello.txt + +The operation will complete. Notice that there is a new file in +/root/crypt that is at least 12288 bytes in size (depending on your +host page size). This is the encrypted underlying file for what you +just wrote. To test reading, from start to finish, you need to clear +the user session keyring: + +keyctl clear @u + +Then umount /mnt/crypt and mount again per the instructions given +above. + +cat /mnt/crypt/hello.txt + + +NOTES + +eCryptfs version 0.1 should only be mounted on (1) empty directories +or (2) directories containing files only created by eCryptfs. If you +mount a directory that has pre-existing files not created by eCryptfs, +then behavior is undefined. Do not run eCryptfs in higher verbosity +levels unless you are doing so for the sole purpose of debugging or +development, since secret values will be written out to the system log +in that case. + + +Mike Halcrow +mhalcrow@us.ibm.com diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 42b95e0ad5..24f3c63b30 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -29,14 +29,6 @@ Who: Adrian Bunk --------------------------- -What: drivers that were depending on OBSOLETE_OSS_DRIVER - (config options already removed) -When: before 2.6.19 -Why: OSS drivers with ALSA replacements -Who: Adrian Bunk - ---------------------------- - What: raw1394: requests of type RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN When: November 2006 Why: Deprecated in favour of the new ioctl-based rawiso interface, which is diff --git a/Documentation/kbuild/kconfig-language.txt b/Documentation/kbuild/kconfig-language.txt index 7f34778dd2..125093c3ef 100644 --- a/Documentation/kbuild/kconfig-language.txt +++ b/Documentation/kbuild/kconfig-language.txt @@ -1,7 +1,7 @@ Introduction ------------ -The configuration database is collection of configuration options +The configuration database is a collection of configuration options organized in a tree structure: +- Code maturity level options diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index e2cbd59cf2..50f4eddf89 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -390,7 +390,7 @@ more details, with real examples. The kernel may be built with several different versions of $(CC), each supporting a unique set of features and options. kbuild provide basic support to check for valid options for $(CC). - $(CC) is useally the gcc compiler, but other alternatives are + $(CC) is usually the gcc compiler, but other alternatives are available. as-option diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 12b3b24bfd..ff571f9298 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -289,9 +289,6 @@ and is between 256 and 4096 characters. It is defined in the file autotest [IA64] - awe= [HW,OSS] AWE32/SB32/AWE64 wave table synth - Format: ,, - aztcd= [HW,CD] Aztech CD268 CDROM driver Format: ,0x79 (?) @@ -536,10 +533,6 @@ and is between 256 and 4096 characters. It is defined in the file Default value is 0. Value can be changed at runtime via /selinux/enforce. - es1370= [HW,OSS] - Format: [,] - See also header of sound/oss/es1370.c. - es1371= [HW,OSS] Format: ,[,[]] See also header of sound/oss/es1371.c. @@ -580,9 +573,6 @@ and is between 256 and 4096 characters. It is defined in the file gscd= [HW,CD] Format: - gus= [HW,OSS] - Format: ,,, - gvp11= [HW,SCSI] hashdist= [KNL,NUMA] Large hashes allocated during boot @@ -841,12 +831,6 @@ and is between 256 and 4096 characters. It is defined in the file (machvec) in a generic kernel. Example: machvec=hpzx1_swiotlb - mad16= [HW,OSS] Format: - ,,,,,, - - maui= [HW,OSS] - Format: , - max_loop= [LOOP] Maximum number of loopback devices that can be mounted Format: <1-256> @@ -1114,9 +1098,6 @@ and is between 256 and 4096 characters. It is defined in the file opl3= [HW,OSS] Format: - opl3sa= [HW,OSS] - Format: ,,,,, - opl3sa2= [HW,OSS] Format: ,,,,,,,[,, Run specified binary instead of /init from the ramdisk, @@ -1455,9 +1432,6 @@ and is between 256 and 4096 characters. It is defined in the file sg_def_reserved_size= [SCSI] - sgalaxy= [HW,OSS] - Format: ,,,, - shapers= [NET] Maximal number of shapers. @@ -1598,9 +1572,6 @@ and is between 256 and 4096 characters. It is defined in the file snd-ymfpci= [HW,ALSA] - sonicvibes= [HW,OSS] - Format: - sonycd535= [HW,CD] Format: [,] diff --git a/Documentation/sound/oss/AWE32 b/Documentation/sound/oss/AWE32 deleted file mode 100644 index b5908a66ff..0000000000 --- a/Documentation/sound/oss/AWE32 +++ /dev/null @@ -1,76 +0,0 @@ - Installing and using Creative AWE midi sound under Linux. - -This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and -SB32. - -1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This - is important, because the driver works only with real Creative cards. - -2) The first thing you need to do is re-compile your kernel with support for - your sound card. Run your favourite tool to configure the kernel and when - you get to the "Sound" menu you should enable support for the following: - - Sound card support, - OSS sound modules, - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support, - AWE32 synth - - If your card is "Plug and Play" you will also need to enable these two - options, found under the "Plug and Play configuration" menu: - - Plug and Play support - ISA Plug and Play support - - Now compile and install the kernel in normal fashion. If you don't know - how to do this you can find instructions for this in the README file - located in the root directory of the kernel source. - -3) Before you can start playing midi files you will have to load a sound - bank file. The utility needed for doing this is called "sfxload", and it - is one of the utilities found in a package called "awesfx". If this - package is not available in your distribution you can download the AWE - snapshot from Creative Labs Open Source website: - - http://www.opensource.creative.com/snapshot.html - - Once you have unpacked the AWE snapshot you will see a "awesfx" - directory. Follow the instructions in awesfx/docs/INSTALL to install the - utilities in this package. After doing this, sfxload should be installed - as: - - /usr/local/bin/sfxload - - To enable AWE general midi synthesis you should also get the sound bank - file for general midi from: - - http://members.xoom.com/yar/synthgm.sbk.gz - - Copy it to a directory of your choice, and unpack it there. - -4) Edit /etc/modprobe.conf, and insert the following lines at the end of the - file: - - alias sound-slot-0 sb - alias sound-service-0-1 awe_wave - install awe_wave /sbin/modprobe --first-time -i awe_wave && /usr/local/bin/sfxload PATH_TO_SOUND_BANK_FILE - - You will of course have to change "PATH_TO_SOUND_BANK_FILE" to the full - path of the sound bank file. That will enable the Sound Blaster and AWE - wave synthesis. To play midi files you should get one of these programs if - you don't already have them: - - Playmidi: http://playmidi.openprojects.net - - AWEMidi Player (drvmidi) Included in the previously mentioned AWE - snapshot. - - You will probably have to pass the "-e" switch to playmidi to have it use - your midi device. drvmidi should work without switches. - - If something goes wrong please e-mail me. All comments and suggestions are - welcome. - - Yaroslav Rosomakho (alons55@dialup.ptt.ru) - http://www.yar.opennet.ru - -Last Updated: Feb 3 2001 diff --git a/Documentation/sound/oss/CMI8338 b/Documentation/sound/oss/CMI8338 deleted file mode 100644 index 387d058c3f..0000000000 --- a/Documentation/sound/oss/CMI8338 +++ /dev/null @@ -1,85 +0,0 @@ -Audio driver for CM8338/CM8738 chips by Chen-Li Tien - - -HARDWARE SUPPORTED -================================================================================ -C-Media CMI8338 -C-Media CMI8738 -On-board C-Media chips - - -STEPS TO BUILD DRIVER -================================================================================ - - 1. Backup the Config.in and Makefile in the sound driver directory - (/usr/src/linux/driver/sound). - The Configure.help provide help when you config driver in step - 4, please backup the original one (/usr/src/linux/Document) and - copy this file. - The cmpci is document for the driver in detail, please copy it - to /usr/src/linux/Document/sound so you can refer it. Backup if - there is already one. - - 2. Extract the tar file by 'tar xvzf cmpci-xx.tar.gz' in the above - directory. - - 3. Change directory to /usr/src/linux - - 4. Config cm8338 driver by 'make menuconfig', 'make config' or - 'make xconfig' command. - - 5. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI - driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested. - For driver option, please refer 'DRIVER PARAMETER' - - 6. Compile the kernel if necessary. - - 7. Compile the modules by 'make modules'. - - 8. Install the modules by 'make modules_install' - - -INSTALL DRIVER -================================================================================ - - 1. Before first time to run the driver, create module dependency by - 'depmod -a' - - 2. To install the driver manually, enter 'modprobe cmpci'. - - 3. Driver installation for various distributions: - - a. Slackware 4.0 - Add the 'modprobe cmpci' command in your /etc/rc.d/rc.modules - file.so you can start the driver automatically each time booting. - - b. Caldera OpenLinux 2.2 - Use LISA to load the cmpci module. - - c. RedHat 6.0 and S.u.S.E. 6.1 - Add following command in /etc/conf.modules: - - alias sound cmpci - - also visit http://www.cmedia.com.tw for installation instruction. - -DRIVER PARAMETER -================================================================================ - - Some functions for the cm8738 can be configured in Kernel Configuration - or modules parameters. Set these parameters to 1 to enable. - - mpuio: I/O ports base for MPU-401, 0 if disabled. - fmio: I/O ports base for OPL-3, 0 if disabled. - spdif_inverse:Inverse the S/PDIF-in signal, this depends on your - CD-ROM or DVD-ROM. - spdif_loop: Enable S/PDIF loop, this route S/PDIF-in to S/PDIF-out - directly. - speakers: Number of speakers used. - use_line_as_rear:Enable this if you want to use line-in as - rear-out. - use_line_as_bass:Enable this if you want to use line-in as - bass-out. - joystick: Enable joystick. You will need to install Linux joystick - driver. - diff --git a/Documentation/sound/oss/INSTALL.awe b/Documentation/sound/oss/INSTALL.awe deleted file mode 100644 index 310f42ca1e..0000000000 --- a/Documentation/sound/oss/INSTALL.awe +++ /dev/null @@ -1,134 +0,0 @@ -================================================================ - INSTALLATION OF AWE32 SOUND DRIVER FOR LINUX - Takashi Iwai -================================================================ - ----------------------------------------------------------------- -* Attention to SB-PnP Card Users - -If you're using PnP cards, the initialization of PnP is required -before loading this driver. You have now three options: - 1. Use isapnptools. - 2. Use in-kernel isapnp support. - 3. Initialize PnP on DOS/Windows, then boot linux by loadlin. -In this document, only the case 1 case is treated. - ----------------------------------------------------------------- -* Installation on Red Hat 5.0 Sound Driver - -Please use install-rh.sh under RedHat5.0 directory. -DO NOT USE install.sh below. -See INSTALL.RH for more details. - ----------------------------------------------------------------- -* Installation/Update by Shell Script - - 1. Become root - - % su - - 2. If you have never configured the kernel tree yet, run make config - once (to make dependencies and symlinks). - - # cd /usr/src/linux - # make xconfig - - 3. Run install.sh script - - # sh ./install.sh - - 4. Configure your kernel - - (for Linux 2.[01].x user) - # cd /usr/src/linux - # make xconfig (or make menuconfig) - - (for Linux 1.2.x user) - # cd /usr/src/linux - # make config - - Answer YES to both "lowlevel drivers" and "AWE32 wave synth" items - in Sound menu. ("lowlevel drivers" will appear only in 2.x - kernel.) - - 5. Make your kernel (and modules), and install them as usual. - - 5a. make kernel image - # make zImage - - 5b. make modules and install them - # make modules && make modules_install - - 5c. If you're using lilo, copy the kernel image and run lilo. - Otherwise, copy the kernel image to suitable directory or - media for your system. - - 6. Reboot the kernel if necessary. - - If you updated only the modules, you don't have to reboot - the system. Just remove the old sound modules here. - in - # rmmod sound.o (linux-2.0 or OSS/Free) - # rmmod awe_wave.o (linux-2.1) - - 7. If your AWE card is a PnP and not initialized yet, you'll have to - do it by isapnp tools. Otherwise, skip to 8. - - This section described only a brief explanation. For more - details, please see the AWE64-Mini-HOWTO or isapnp tools FAQ. - - 7a. If you have no isapnp.conf file, generate it by pnpdump. - Otherwise, skip to 7d. - # pnpdump > /etc/isapnp.conf - - 7b. Edit isapnp.conf file. Comment out the appropriate - lines containing desirable I/O ports, DMA and IRQs. - Don't forget to enable (ACT Y) line. - - 7c. Add two i/o ports (0xA20 and 0xE20) in WaveTable part. - ex) - (CONFIGURE CTL0048/58128 (LD 2 - # ANSI string -->WaveTable<-- - (IO 0 (BASE 0x0620)) - (IO 1 (BASE 0x0A20)) - (IO 2 (BASE 0x0E20)) - (ACT Y) - )) - - 7d. Load the config file. - CAUTION: This will reset all PnP cards! - - # isapnp /etc/isapnp.conf - - 8. Load the sound module (if you configured it as a module): - - for 2.0 kernel or OSS/Free monolithic module: - - # modprobe sound.o - - for 2.1 kernel: - - # modprobe sound - # insmod uart401 - # insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 - (These values depend on your settings.) - # insmod awe_wave - (Be sure to load awe_wave after sb!) - - See Documentation/sound/oss/AWE32 for - more details. - - 9. (only for obsolete systems) If you don't have /dev/sequencer - device file, make it according to Readme.linux file on - /usr/src/linux/drivers/sound. (Run a shell script included in - that file). <-- This file no longer exists in the recent kernels! - - 10. OK, load your own soundfont file, and enjoy MIDI! - - % sfxload synthgm.sbk - % drvmidi foo.mid - - 11. For more advanced use (eg. dynamic loading, virtual bank and - etc.), please read the awedrv FAQ or the instructions in awesfx - and awemidi packages. - -Good luck! diff --git a/Documentation/sound/oss/MAD16 b/Documentation/sound/oss/MAD16 deleted file mode 100644 index 865dbd8487..0000000000 --- a/Documentation/sound/oss/MAD16 +++ /dev/null @@ -1,56 +0,0 @@ -(This recipe has been edited to update the configuration symbols, - and change over to modprobe.conf for 2.6) - -From: Shaw Carruthers - -I have been using mad16 sound for some time now with no problems, current -kernel 2.1.89 - -lsmod shows: - -mad16 5176 0 -sb 22044 0 [mad16] -uart401 5576 0 [mad16 sb] -ad1848 14176 1 [mad16] -sound 61928 0 [mad16 sb uart401 ad1848] - -.config has: - -CONFIG_SOUND=m -CONFIG_SOUND_ADLIB=m -CONFIG_SOUND_MAD16=m -CONFIG_SOUND_YM3812=m - -modprobe.conf has: - -alias char-major-14-* mad16 -options sb mad16=1 -options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 - - -To get the built in mixer to work this needs to be: - -options adlib_card io=0x388 # FM synthesizer -options sb mad16=1 -options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 - -The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is - ------------------------------------------------------------------------- -The mad16 module in addition supports the following options: - -option: meaning: default: -joystick=0,1 disabled, enabled disabled -cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled - 0x06,0x08,0x0a Mitsumi, Panasonic, - Secondary IDE, Primary IDE -cdport=0x340,0x320, 0x340 - 0x330,0x360 -cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled -cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE -cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic -opl4=0,1 OPL3, OPL4 OPL3 - -for more details see linux/drivers/sound/mad16.c - -Rui Sousa diff --git a/Documentation/sound/oss/Maestro b/Documentation/sound/oss/Maestro deleted file mode 100644 index 4a80eb3f8e..0000000000 --- a/Documentation/sound/oss/Maestro +++ /dev/null @@ -1,123 +0,0 @@ - An OSS/Lite Driver for the ESS Maestro family of sound cards - - Zach Brown, December 1999 - -Driver Status and Availability ------------------------------- - -The most recent version of this driver will hopefully always be available at - http://www.zabbo.net/maestro/ - -I will try and maintain the most recent stable version of the driver -in both the stable and development kernel lines. - -ESS Maestro Chip Family ------------------------ - -There are 3 main variants of the ESS Maestro PCI sound chip. The first -is the Maestro 1. It was originally produced by Platform Tech as the -'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with -0x0100 as the device ID. It was put on some sound boards and a few laptops. -ESS bought the design and cleaned it up as the Maestro 2. This starts -their marking with the ESS vendor ID 0x125D and the 'year' device IDs. -The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. - -The various families of Maestro are mostly identical as far as this -driver is concerned. It doesn't touch the DSP parts that differ (though -it could for FM synthesis). - -Driver OSS Behavior --------------------- - -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. - -The /dev/dsp device exported behaves almost as expected. Playback is -supported in all the various lovely formats. 8/16bit stereo/mono from -8khz to 48khz, and mmap()ing for playback behaves. Capture/recording -is limited due to oddities with the Maestro hardware. One can only -record in 16bit stereo. For recording the maestro uses non interleaved -stereo buffers so that mmap()ing the incoming data does not result in -a ring buffer of LRLR data. mmap()ing of the read buffers is therefore -disallowed until this can be cleaned up. - -/dev/mixer is an interface to the AC'97 codec on the Maestro. It is -worth noting that there are a variety of AC'97s that can be wired to -the Maestro. Which is used is entirely up to the hardware implementor. -This should only be visible to the user by the presence, or lack, of -'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. - -The driver doesn't support MIDI or FM playback at the moment. Typically -the Maestro is wired to an MPU MIDI chip, but some hardware implementations -don't. We need to assemble a white list of hardware implementations that -have MIDI wired properly before we can claim to support it safely. - -Compiling and Installing ------------------------- - -With the drivers inclusion into the kernel, compiling and installing -is the same as most OSS/Lite modular sound drivers. Compilation -of the driver is enabled through the CONFIG_SOUND_MAESTRO variable -in the config system. - -It may be modular or statically linked. If it is modular it should be -installed with the rest of the modules for the kernel on the system. -Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' -should also be added to your module configs (typically /etc/conf.modules) -if you're using modular OSS/Lite sound and want to default to using a -maestro chip. - -As this is a PCI device, the module does not need to be informed of -any IO or IRQ resources it should use, it devines these from the -system. Sometimes, on sucky PCs, the BIOS fails to allocated resources -for the maestro. This will result in a message like: - maestro: PCI subsystem reports IRQ 0, this might not be correct. -from the kernel. Should this happen the sound chip most likely will -not operate correctly. To solve this one has to dig through their BIOS -(typically entered by hitting a hot key at boot time) and figure out -what magic needs to happen so that the BIOS will reward the maestro with -an IRQ. This operation is incredibly system specific, so you're on your -own. Sometimes the magic lies in 'PNP Capable Operating System' settings. - -There are very few options to the driver. One is 'debug' which will -tell the driver to print minimal debugging information as it runs. This -can be collected with 'dmesg' or through the klogd daemon. - -The other, more interesting option, is 'dsps_order'. Typically at -install time the driver will only register one available /dev/dsp device -for its use. The 'dsps_order' module parameter allows for more devices -to be allocated, as a power of two. Up to 4 devices can be registered -( dsps_order=2 ). These devices act as fully distinct units and use -separate channels in the maestro. - -Power Management ----------------- - -As of version 0.14, this driver has a minimal understanding of PCI -Power Management. If it finds a valid power management capability -on the PCI device it will attempt to use the power management -functions of the maestro. It will only do this on Maestro 2Es and -only on machines that are known to function well. You can -force the use of power management by setting the 'use_pm' module -option to 1, or can disable it entirely by setting it to 0. - -When using power management, the driver does a few things -differently. It will keep the chip in a lower power mode -when the module is inserted but /dev/dsp is not open. This -allows the mixer to function but turns off the clocks -on other parts of the chip. When /dev/dsp is opened the chip -is brought into full power mode, and brought back down -when it is closed. It also powers down the chip entirely -when the module is removed or the machine is shutdown. This -can have nonobvious consequences. CD audio may not work -after a power managing driver is removed. Also, software that -doesn't understand power management may not be able to talk -to the powered down chip until the machine goes through a hard -reboot to bring it back. - -.. more details .. ------------------- - -drivers/sound/maestro.c contains comments that hopefully explain -the maestro implementation. diff --git a/Documentation/sound/oss/Maestro3 b/Documentation/sound/oss/Maestro3 deleted file mode 100644 index a113718e80..0000000000 --- a/Documentation/sound/oss/Maestro3 +++ /dev/null @@ -1,92 +0,0 @@ - An OSS/Lite Driver for the ESS Maestro3 family of sound chips - - Zach Brown, January 2001 - -Driver Status and Availability ------------------------------- - -The most recent version of this driver will hopefully always be available at - http://www.zabbo.net/maestro3/ - -I will try and maintain the most recent stable version of the driver -in both the stable and development kernel lines. - -Historically I've sucked pretty hard at actually doing that, however. - -ESS Maestro3 Chip Family ------------------------ - -The 'Maestro3' is much like the Maestro2 chip. The noted improvement -is the removal of the silicon in the '2' that did PCM mixing. All that -work is now done through a custom DSP called the ASSP, the Asynchronus -Specific Signal Processor. - -The 'Allegro' is a baby version of the Maestro3. I'm not entirely clear -on the extent of the differences, but the driver supports them both :) - -The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998, -both under ESS's vendor ID of 0x125D. The Maestro3 can also show up as -0x199a when hardware strapping is used. - -The chip can also act as a multi function device. The modem IDs follow -the audio multimedia device IDs. (so the modem part of an Allegro shows -up as 0x1989) - -Driver OSS Behavior --------------------- - -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. - -The /dev/dsp device exported behaves as expected. Playback is -supported in all the various lovely formats. 8/16bit stereo/mono from -8khz to 48khz, with both read()/write(), and mmap(). - -/dev/mixer is an interface to the AC'97 codec on the Maestro3. It is -worth noting that there are a variety of AC'97s that can be wired to -the Maestro3. Which is used is entirely up to the hardware implementor. -This should only be visible to the user by the presence, or lack, of -'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. -The Allegro has an onchip AC'97. - -The driver doesn't support MIDI or FM playback at the moment. - -Compiling and Installing ------------------------- - -With the drivers inclusion into the kernel, compiling and installing -is the same as most OSS/Lite modular sound drivers. Compilation -of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable -in the config system. - -It may be modular or statically linked. If it is modular it should be -installed with the rest of the modules for the kernel on the system. -Typically this will be in /lib/modules/ somewhere. 'alias sound-slot-0 -maestro3' should also be added to your module configs (typically -/etc/modprobe.conf) if you're using modular OSS/Lite sound and want to -default to using a maestro3 chip. - -There are very few options to the driver. One is 'debug' which will -tell the driver to print minimal debugging information as it runs. This -can be collected with 'dmesg' or through the klogd daemon. - -One is 'external_amp', which tells the driver to attempt to enable -an external amplifier. This defaults to '1', you can tell the driver -not to bother enabling such an amplifier by setting it to '0'. - -And the last is 'gpio_pin', which tells the driver which GPIO pin number -the external amp uses (0-15), The Allegro uses 8 by default, all others 1. -If everything loads correctly and seems to be working but you get no sound, -try tweaking this value. - -Systems known to need a different value - Panasonic ToughBook CF-72: gpio_pin=13 - -Power Management ----------------- - -This driver has a minimal understanding of PCI Power Management. It will -try and power down the chip when the system is suspended, and power -it up with it is resumed. It will also try and power down the chip -when the machine is shut down. diff --git a/Documentation/sound/oss/NEWS b/Documentation/sound/oss/NEWS deleted file mode 100644 index a81e0ef72a..0000000000 --- a/Documentation/sound/oss/NEWS +++ /dev/null @@ -1,42 +0,0 @@ -Linux 2.4 Sound Changes -2000-September-25 -Christoph Hellwig, - - - -=== isapnp support - -The Linux 2.4 Kernel does have reliable in-kernel isapnp support. -Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically -detecting and configuring isapnp devices. -If you have a not yet supported isapnp soundcard, mail me the content -of '/proc/isapnp' on your system and some information about your card -and its driver(s) so I can try to get isapnp working for it. - - - -=== soundcard resources on kernel commandline - -Before Linux 2.4 you had to specify the resources for sounddrivers -statically linked into the kernel at compile time -(in make config/menuconfig/xconfig). In Linux 2.4 the resources are -now specified at the boot-time kernel commandline (e.g. the lilo -'append=' line or everything that's after the kernel name in grub). -Read the Configure.help entry for your card for the parameters. - - -=== softoss is gone - -In Linux 2.4 the softoss in-kernel software synthesizer is no more aviable. -Use a user space software synthesizer like timidity instead. - - - -=== /dev/sndstat and /proc/sound are gone - -In older Linux versions those files exported some information about the -OSS/Free configuration to userspace. In Linux 2.3 they were removed because -they did not support the growing number of pci soundcards and there were -some general problems with this interface. - - diff --git a/Documentation/sound/oss/OPL3-SA b/Documentation/sound/oss/OPL3-SA deleted file mode 100644 index 66a91835d9..0000000000 --- a/Documentation/sound/oss/OPL3-SA +++ /dev/null @@ -1,52 +0,0 @@ -OPL3-SA1 sound driver (opl3sa.o) - ---- -Note: This howto only describes how to setup the OPL3-SA1 chip; this info -does not apply to the SA2, SA3, or SA4. ---- - -The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and -it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 -and OPL3 FM Synth capabilities. - -You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or -CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. - -You'll need to know all of the relevant info (irq, dma, and io port) for the -chip's WSS mode, since that is the mode the kernel sound driver uses, and of -course you'll also need to know about where the MPU401 and OPL3 ports and -IRQs are if you want to use those. - -Here's the skinny on how to load it as a module: - - modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 - -Module options in detail: - - io: This is the WSS's port base. - irq: This is the WSS's IRQ. - dma: This is the WSS's DMA line. In my BIOS setup screen this was - listed as "WSS Play DMA" - dma2: This is the WSS's secondary DMA line. My BIOS calls it the - "WSS capture DMA" - - mpu_io: This is the MPU401's port base. - mpu_irq: This is the MPU401's IRQ. - -If you'd like to use the OPL3 FM Synthesizer, make sure you enable -CONFIG_SOUND_YM3812 (in 'make config'). That'll build the opl3.o module. - -Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. - -You can also use the SoftOSS software synthesizer instead of the builtin OPL3. -Here's how: - -Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. - -If you said yes, the software synth is available once you boot your new -kernel. - -If you chose to build it as a module, just insmod the resulting softoss2.o - -Questions? Comments? - diff --git a/Documentation/sound/oss/README.awe b/Documentation/sound/oss/README.awe deleted file mode 100644 index 80054cd8fc..0000000000 --- a/Documentation/sound/oss/README.awe +++ /dev/null @@ -1,218 +0,0 @@ -================================================================ - AWE32 Sound Driver for Linux / FreeBSD - version 0.4.3; Nov. 1, 1998 - - Takashi Iwai -================================================================ - -* GENERAL NOTES - -This is a sound driver extension for SoundBlaster AWE32 and other -compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable -the wave synth operations. The driver is provided for Linux 1.2.x -and 2.[012].x kernels, as well as FreeBSD, on Intel x86 and DEC -Alpha systems. - -This driver was written by Takashi Iwai , -and provided "as is". The original source (awedrv-0.4.3.tar.gz) and -binary packages are available on the following URL: - http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ -Note that since the author is apart from this web site, the update is -not frequent now. - - -* NOTE TO LINUX USERS - -To enable this driver on linux-2.[01].x kernels, you need turn on -"AWE32 synth" options in sound menu when configure your linux kernel -and modules. The precise installation procedure is described in the -AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. - -If you're using PnP cards, the card must be initialized before loading -the sound driver. There're several options to do this: - - Initialize the card via ISA PnP tools, and load the sound module. - - Initialize the card on DOS, and load linux by loadlin.exe - - Use PnP kernel driver (for Linux-2.x.x) -The detailed instruction for the solution using isapnp tools is found -in many documents like above. A brief instruction is also included in -the installation document of this package. -For PnP driver project, please refer to the following URL: - http://www-jcr.lmh.ox.ac.uk/~pnp/ - - -* USING THE DRIVER - -The awedrv has several different playing modes to realize easy channel -allocation for MIDI songs. To hear the exact sound quality, you need -to obtain the extended sequencer program, drvmidi or playmidi-2.5. - -For playing MIDI files, you *MUST* load the soundfont file on the -driver previously by sfxload utility. Otherwise you'll here no sounds -at all! All the utilities and driver source packages are found in the -above URL. The sfxload program is included in the package -awesfx-0.4.3.tgz. Binary packages are available there, too. See the -instruction in each package for installation. - -Loading a soundfont file is very simple. Just execute the command - - % sfxload synthgm.sbk - -Then, sfxload transfers the file "synthgm.sbk" to the driver. -Both SF1 and SF2 formats are accepted. - -Now you can hear midi musics by a midi player. - - % drvmidi foo.mid - -If you run MIDI player after MOD player, you need to load soundfont -files again, since MOD player programs clear the previous loaded -samples by their own data. - -If you have only 512kb on the sound card, I recommend to use dynamic -sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is -available in most midi files. - - % sfxload synthgm - % drvmidi -L 2mbgmgs foo.mid - -This makes a big difference (believe me)! For more details, please -refer to the FAQ list which is available on the URL above. - -The current chorus, reverb and equalizer status can be changed by -aweset utility program (included in awesfx package). Note that -some awedrv-native programs (like drvmidi and xmp) will change the -current settings by themselves. The aweset program is effective -only for other programs like playmidi. - -Enjoy. - - -* COMPILE FLAGS - -Compile conditions are defined in awe_config.h. - -[Compatibility Conditions] -The following flags are defined automatically when using installation -shell script. - -- AWE_MODULE_SUPPORT - indicates your Linux kernel supports module for each sound card - (in recent 2.1 or 2.2 kernels and unofficial patched 2.0 kernels - as distributed in the RH5.0 package). - This flag is automatically set when you're using 2.1.x kernels. - You can pass the base address and memory size via the following - module options, - io = base I/O port address (eg. 0x620) - memsize = DRAM size in kilobytes (eg. 512) - As default, AWE driver probes these values automatically. - - -[Hardware Conditions] -You DON'T have to define the following two values. -Define them only when the driver couldn't detect the card properly. - -- AWE_DEFAULT_BASE_ADDR (default: not defined) - specifies the base port address of your AWE32 card. - 0 means to autodetect the address. - -- AWE_DEFAULT_MEM_SIZE (default: not defined) - specifies the memory size of your AWE32 card in kilobytes. - -1 means to autodetect its size. - - -[Sample Table Size] -From ver.0.4.0, sample tables are allocated dynamically (except -Linux-1.2.x system), so you need NOT to touch these parameters. -Linux-1.2.x users may need to increase these values to appropriate size -if the sound card is equipped with more DRAM. - -- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS - - -[Other Conditions] - -- AWE_ALWAYS_INIT_FM (default: not defined) - indicates the AWE driver always initialize FM passthrough even - without DRAM on board. Emu8000 chip has a restriction for playing - samples on DRAM that at least two channels must be occupied as - passthrough channels. - -- AWE_DEBUG_ON (default: defined) - turns on debugging messages if defined. - -- AWE_HAS_GUS_COMPATIBILITY (default: defined) - Enables GUS compatibility mode if defined, reading GUS patches and - GUS control commands. Define this option to use GMOD or other - GUS module players. - -- CONFIG_AWE32_MIDIEMU (default: defined) - Adds a MIDI emulation device by Emu8000 wavetable. The emulation - device can be accessed as an external MIDI, and sends the MIDI - control codes directly. XG and GS sysex/NRPN are accepted. - No MIDI input is supported. - -- CONFIG_AWE32_MIXER (default: not defined) - Adds a mixer device for AWE32 bass/treble equalizer control. - You can access this device using /dev/mixer?? (usually mixer01). - -- AWE_USE_NEW_VOLUME_CALC (default: defined) - Use the new method to calculate the volume change as compatible - with DOS/Win drivers. This option can be toggled via aweset - program, or drvmidi player. - -- AWE_CHECK_VTARGET (default: defined) - Check the current volume target value when searching for an - empty channel to allocate a new voice. This is experimentally - implemented in this version. (probably, this option doesn't - affect the sound quality severely...) - -- AWE_ALLOW_SAMPLE_SHARING (default: defined) - Allow sample sharing for differently loaded patches. - This function is available only together with awesfx-0.4.3p3. - Note that this is still an experimental option. - -- DEF_FM_CHORUS_DEPTH (default: 0x10) - The default strength to be sent to the chorus effect engine. - From 0 to 0xff. Larger numbers may often cause weird sounds. - -- DEF_FM_REVERB_DEPTH (default: 0x10) - The default strength to be sent to the reverb effect engine. - From 0 to 0xff. Larger numbers may often cause weird sounds. - - -* ACKNOWLEDGMENTS - -Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice -on programming of AWE32. Much code is brought from his AWE32-native -MOD player, ALMP. -The port of awedrv to FreeBSD is done by Randall Hopper -(rhh@ct.picker.com). -The new volume calculation routine was derived from Mark Weaver's -ADIP compatible routines. -I also thank linux-awe-ml members for their efforts -to reboot their system many times :-) - - -* TODO'S - -- Complete DOS/Win compatibility -- DSP-like output - - -* COPYRIGHT - -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. diff --git a/Documentation/sound/oss/Wavefront b/Documentation/sound/oss/Wavefront deleted file mode 100644 index 16f57ea430..0000000000 --- a/Documentation/sound/oss/Wavefront +++ /dev/null @@ -1,339 +0,0 @@ - An OSS/Free Driver for WaveFront soundcards - (Turtle Beach Maui, Tropez, Tropez Plus) - - Paul Barton-Davis, July 1998 - - VERSION 0.2.5 - -Driver Status -------------- - -Requires: Kernel 2.1.106 or later (the driver is included with kernels -2.1.109 and above) - -As of 7/22/1998, this driver is currently in *BETA* state. This means -that it compiles and runs, and that I use it on my system (Linux -2.1.106) with some reasonably demanding applications and uses. I -believe the code is approaching an initial "finished" state that -provides bug-free support for the Tropez Plus. - -Please note that to date, the driver has ONLY been tested on a Tropez -Plus. I would very much like to hear (and help out) people with Tropez -and Maui cards, since I think the driver can support those cards as -well. - -Finally, the driver has not been tested (or even compiled) as a static -(non-modular) part of the kernel. Alan Cox's good work in modularizing -OSS/Free for Linux makes this rather unnecessary. - -Some Questions --------------- - -********************************************************************** -0) What does this driver do that the maui driver did not ? -********************************************************************** - -* can fully initialize a WaveFront card from cold boot - no DOS - utilities needed -* working patch/sample/program loading and unloading (the maui - driver didn't document how to make this work, and assumed - user-level preparation of the patch data for writing - to the board. ick.) -* full user-level access to all WaveFront commands -* for the Tropez Plus, (primitive) control of the YSS225 FX processor -* Virtual MIDI mode supported - 2 MIDI devices accessible via the - WaveFront's MPU401/UART emulation. One - accesses the WaveFront synth, the other accesses the - external MIDI connector. Full MIDI read/write semantics - for both devices. -* OSS-compliant /dev/sequencer interface for the WaveFront synth, - including native and GUS-format patch downloading. -* semi-intelligent patch management (prototypical at this point) - -********************************************************************** -1) What to do about MIDI interfaces ? -********************************************************************** - -The Tropez Plus (and perhaps other WF cards) can in theory support up -to 2 physical MIDI interfaces. One of these is connected to the -ICS2115 chip (the WaveFront synth itself) and is controlled by -MPU/UART-401 emulation code running as part of the WaveFront OS. The -other is controlled by the CS4232 chip present on the board. However, -physical access to the CS4232 connector is difficult, and it is -unlikely (though not impossible) that you will want to use it. - -An older version of this driver introduced an additional kernel config -variable which controlled whether or not the CS4232 MIDI interface was -configured. Because of Alan Cox's work on modularizing the sound -drivers, and now backporting them to 2.0.34 kernels, there seems to be -little reason to support "static" configuration variables, and so this -has been abandoned in favor of *only* module parameters. Specifying -"mpuio" and "mpuirq" for the cs4232 parameter will result in the -CS4232 MIDI interface being configured; leaving them unspecified will -leave it unconfigured (and thus unusable). - -BTW, I have heard from one Tropez+ user that the CS4232 interface is -more reliable than the ICS2115 one. I have had no problems with the -latter, and I don't have the right cable to test the former one -out. Reports welcome. - -********************************************************************** -2) Why does line XXX of the code look like this .... ? -********************************************************************** - -Either because it's not finished yet, or because you're a better coder -than I am, or because you don't understand some aspect of how the card -or the code works. - -I absolutely welcome comments, criticisms and suggestions about the -design and implementation of the driver. - -********************************************************************** -3) What files are included ? -********************************************************************** - - drivers/sound/README.wavefront -- this file - - drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers - needed to make the rest of this work - DO NOT USE IF YOU'VE APPLIED THEM - BEFORE, OR HAVE 2.1.109 OR ABOVE - - drivers/sound/wavfront.c -- the driver - drivers/sound/ys225.h -- data declarations for FX config - drivers/sound/ys225.c -- data definitions for FX config - drivers/sound/wf_midi.c -- the "uart401" driver - to support virtual MIDI mode. - include/wavefront.h -- the header file - Documentation/sound/oss/Tropez+ -- short docs on configuration - -********************************************************************** -4) How do I compile/install/use it ? -********************************************************************** - -PART ONE: install the source code into your sound driver directory - - cd - tar -zxvf - -PART TWO: apply the patches - - DO THIS ONLY IF YOU HAVE A KERNEL VERSION BELOW 2.1.109 - AND HAVE NOT ALREADY INSTALLED THE PATCH(ES). - - cd drivers/sound - patch < wavefront.patch - -PART THREE: configure your kernel - - cd - make xconfig (or whichever config option you use) - - - choose YES for Sound Support - - choose MODULE (M) for OSS Sound Modules - - choose MODULE(M) to YM3812/OPL3 support - - choose MODULE(M) for WaveFront support - - choose MODULE(M) for CS4232 support - - - choose "N" for everything else (unless you have other - soundcards you want support for) - - - make boot - . - . - . - - make modules - . - . - . - make modules_install - -Here's my autoconf.h SOUND section: - -/* - * Sound - */ -#define CONFIG_SOUND 1 -#undef CONFIG_SOUND_OSS -#define CONFIG_SOUND_OSS_MODULE 1 -#undef CONFIG_SOUND_PAS -#undef CONFIG_SOUND_SB -#undef CONFIG_SOUND_ADLIB -#undef CONFIG_SOUND_GUS -#undef CONFIG_SOUND_MPU401 -#undef CONFIG_SOUND_PSS -#undef CONFIG_SOUND_MSS -#undef CONFIG_SOUND_SSCAPE -#undef CONFIG_SOUND_TRIX -#undef CONFIG_SOUND_MAD16 -#undef CONFIG_SOUND_WAVEFRONT -#define CONFIG_SOUND_WAVEFRONT_MODULE 1 -#undef CONFIG_SOUND_CS4232 -#define CONFIG_SOUND_CS4232_MODULE 1 -#undef CONFIG_SOUND_MAUI -#undef CONFIG_SOUND_SGALAXY -#undef CONFIG_SOUND_OPL3SA1 -#undef CONFIG_SOUND_SOFTOSS -#undef CONFIG_SOUND_YM3812 -#define CONFIG_SOUND_YM3812_MODULE 1 -#undef CONFIG_SOUND_VMIDI -#undef CONFIG_SOUND_UART6850 -/* - * Additional low level sound drivers - */ -#undef CONFIG_LOWLEVEL_SOUND - -************************************************************ -6) How do I configure my card ? -************************************************************ - -You need to edit /etc/modprobe.conf. Here's mine (edited to show the -relevant details): - - # Sound system - alias char-major-14-* wavefront - alias synth0 wavefront - alias mixer0 cs4232 - alias audio0 cs4232 - install wavefront /sbin/modprobe cs4232 && /sbin/modprobe -i wavefront && /sbin/modprobe opl3 - options wavefront io=0x200 irq=9 - options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 - options opl3 io=0x388 - -Things to note: - - the wavefront options "io" and "irq" ***MUST*** match the "synthio" - and "synthirq" cs4232 options. - - you can do without the opl3 module if you don't - want to use the OPL/[34] FM synth on the soundcard - - the opl3 io parameter is conventionally not adjustable. - In theory, any not-in-use IO port address would work, but - just use 0x388 and stick with the crowd. - -********************************************************************** -7) What about firmware ? -********************************************************************** - -Turtle Beach have not given me permission to distribute their firmware -for the ICS2115. However, if you have a WaveFront card, then you -almost certainly have the firmware, and if not, its freely available -on their website, at: - - http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus - -The file is called WFOS2001.MOT (for the Tropez+). - -This driver, however, doesn't use the pure firmware as distributed, -but instead relies on a somewhat processed form of it. You can -generate this very easily. Following an idea from Andrew Veliath's -Pinnacle driver, the following flex program will generate the -processed version: - ----- cut here ------------------------- -%option main -%% -^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); -<> { fputc ('\0', stdout); return; } -\n {} -. {} ----- cut here ------------------------- - -To use it, put the above in file (say, ws.l) compile it like this: - - shell> flex -ows.c ws.l - shell> cc -o ws ws.c - -and then use it like this: - - ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os - -If you put it somewhere else, you'll always have to use the wf_ospath -module parameter (see below) or alter the source code. - -********************************************************************** -7) How do I get it working ? -********************************************************************** - -Optionally, you can reboot with the "new" kernel (even though the only -changes have really been made to a module). - -Then, as root do: - - modprobe wavefront - -You should get something like this in /var/log/messages: - - WaveFront: firmware 1.20 already loaded. - -or - - WaveFront: no response to firmware probe, assume raw. - -then: - - WaveFront: waiting for memory configuration ... - WaveFront: hardware version 1.64 - WaveFront: available DRAM 8191k - WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty - WaveFront: 128 programs slots in use - WaveFront: 256 patch slots filled, 142 in use - -The whole process takes about 16 seconds, the longest waits being -after reporting the hardware version (during the firmware download), -and after reporting program status (during patch status inquiry). Its -shorter (about 10 secs) if the firmware is already loaded (i.e. only -warm reboots since the last firmware load). - -The "available DRAM" line will vary depending on how much added RAM -your card has. Mine has 8MB. - -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 -difference between sending MIDI to the WaveFront and using the OPL/3, -which is the default (I think ...). If you have an external synth(s) -hooked to the soundcard, you can use "-e" to route to the -external synth(s) (in theory, -D 1 should work as well, but I think -there is a bug in playmidi which prevents this from doing what it -should). - -********************************************************************** -8) What are the module parameters ? -********************************************************************** - -Its best to read wavefront.c for this, but here is a summary: - -integers: - wf_raw - if set, ignore apparent presence of firmware - loaded onto the ICS2115, reset the whole - board, and initialize it from scratch. (default = 0) - - fx_raw - if set, always initialize the YSS225 processor - on the Tropez plus. (default = 1) - - < The next 4 are basically for kernel hackers to allow - tweaking the driver for testing purposes. > - - wait_usecs - loop timer used when waiting for - status conditions on the board. - The default is 150. - - debug_default - debugging flags. See sound/wavefront.h - for WF_DEBUG_* values. Default is zero. - Setting this allows you to debug the - driver during module installation. -strings: - ospath - path to get to the pre-processed OS firmware. - (default: /etc/sound/wavefront.os) - -********************************************************************** -9) Who should I contact if I have problems? -********************************************************************** - -Just me: Paul Barton-Davis - - diff --git a/Documentation/sound/oss/es1370 b/Documentation/sound/oss/es1370 deleted file mode 100644 index 7b38b1a096..0000000000 --- a/Documentation/sound/oss/es1370 +++ /dev/null @@ -1,70 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -This soundcard does not have any hardware MIDI synthesizer; -MIDI synthesis has to be done in software. To allow this -the driver/soundcard supports two PCM (/dev/dsp) interfaces. -The second one goes to the mixer "synth" setting and supports -only a limited set of sampling rates (44100, 22050, 11025, 5512). -By setting lineout to 1 on the driver command line -(eg. insmod es1370 lineout=1) it is even possible on some -cards to convert the LINEIN jack into a second LINEOUT jack, thus -making it possible to output four independent audio channels! - -There is a freely available software package that allows -MIDI file playback on this soundcard called Timidity. -See http://www.cgs.fi/~tt/timidity/. - - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff --git a/Documentation/sound/oss/rme96xx b/Documentation/sound/oss/rme96xx deleted file mode 100644 index 87d7b7b65f..0000000000 --- a/Documentation/sound/oss/rme96xx +++ /dev/null @@ -1,767 +0,0 @@ -Beta release of the rme96xx (driver for RME 96XX cards like the -"Hammerfall" and the "Hammerfall light") - -Important: The driver module has to be installed on a freshly rebooted system, -otherwise the driver might not be able to acquire its buffers. - -features: - - - OSS programming interface (i.e. runs with standard OSS soundsoftware) - - OSS/Multichannel interface (OSS multichannel is done by just aquiring - more than 2 channels). The driver does not use more than one device - ( yet .. this feature may be implemented later ) - - more than one RME card supported - -The driver uses a specific multichannel interface, which I will document -when the driver gets stable. (take a look at the defines in rme96xx.h, -which adds blocked multichannel formats i.e instead of -lrlrlrlr --> llllrrrr etc. - -Use the "rmectrl" programm to look at the status of the card .. -or use xrmectrl, a GUI interface for the ctrl program. - -What you can do with the rmectrl program is to set the stereo device for -OSS emulation (e.g. if you use SPDIF out). - -You do: - -./ctrl offset 24 24 - -which makes the stereo device use channels 25 and 26. - -Guenter Geiger - -copy the first part of the attached source code into rmectrl.c -and the second part into xrmectrl (or get the program from -http://gige.xdv.org/pages/soft/pages/rme) - -to compile: gcc -o rmectrl rmectrl.c ------------------------------- snip ------------------------------------ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "rme96xx.h" - -/* - remctrl.c - (C) 2000 Guenter Geiger - HP20020201 - Heiko Purnhagen -*/ - -/* # define DEVICE_NAME "/dev/mixer" */ -# define DEVICE_NAME "/dev/mixer1" - - -void usage(void) -{ - fprintf(stderr,"usage: rmectrl [/dev/mixer] [command [options]]\n\n"); - fprintf(stderr,"where command is one of:\n"); - fprintf(stderr," help show this help\n"); - fprintf(stderr," status show status bits\n"); - fprintf(stderr," control show control bits\n"); - fprintf(stderr," mix show mixer/offset status\n"); - fprintf(stderr," master set sync master\n"); - fprintf(stderr," pro set spdif out pro\n"); - fprintf(stderr," emphasis set spdif out emphasis\n"); - fprintf(stderr," dolby set spdif out no audio\n"); - fprintf(stderr," optout set spdif out optical\n"); - fprintf(stderr," wordclock set sync wordclock\n"); - fprintf(stderr," spdifin set spdif in (0=optical,1=coax,2=intern)\n"); - fprintf(stderr," syncref set sync source (0=ADAT1,1=ADAT2,2=ADAT3,3=SPDIF)\n"); - fprintf(stderr," adat1cd set ADAT1 on internal CD\n"); - fprintf(stderr," offset set dev (0..3) offset (0..25)\n"); - exit(-1); -} - - -int main(int argc, char* argv[]) -{ - int cards; - int ret; - int i; - double ft; - int fd, fdwr; - int param,orig; - rme_status_t stat; - rme_ctrl_t ctrl; - char *device; - int argidx; - - if (argc < 2) - usage(); - - if (*argv[1]=='/') { - device = argv[1]; - argidx = 2; - } - else { - device = DEVICE_NAME; - argidx = 1; - } - - fprintf(stdout,"mixer device %s\n",device); - if ((fd = open(device,O_RDONLY)) < 0) { - fprintf(stdout,"opening device failed\n"); - exit(-1); - } - - if ((fdwr = open(device,O_WRONLY)) < 0) { - fprintf(stdout,"opening device failed\n"); - exit(-1); - } - - if (argc < argidx+1) - usage(); - - if (!strcmp(argv[argidx],"help")) - usage(); - if (!strcmp(argv[argidx],"-h")) - usage(); - if (!strcmp(argv[argidx],"--help")) - usage(); - - if (!strcmp(argv[argidx],"status")) { - ioctl(fd,SOUND_MIXER_PRIVATE2,&stat); - fprintf(stdout,"stat.irq %d\n",stat.irq); - fprintf(stdout,"stat.lockmask %d\n",stat.lockmask); - fprintf(stdout,"stat.sr48 %d\n",stat.sr48); - fprintf(stdout,"stat.wclock %d\n",stat.wclock); - fprintf(stdout,"stat.bufpoint %d\n",stat.bufpoint); - fprintf(stdout,"stat.syncmask %d\n",stat.syncmask); - fprintf(stdout,"stat.doublespeed %d\n",stat.doublespeed); - fprintf(stdout,"stat.tc_busy %d\n",stat.tc_busy); - fprintf(stdout,"stat.tc_out %d\n",stat.tc_out); - fprintf(stdout,"stat.crystalrate %d (0=64k 3=96k 4=88.2k 5=48k 6=44.1k 7=32k)\n",stat.crystalrate); - fprintf(stdout,"stat.spdif_error %d\n",stat.spdif_error); - fprintf(stdout,"stat.bufid %d\n",stat.bufid); - fprintf(stdout,"stat.tc_valid %d\n",stat.tc_valid); - exit (0); - } - - if (!strcmp(argv[argidx],"control")) { - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - fprintf(stdout,"ctrl.start %d\n",ctrl.start); - fprintf(stdout,"ctrl.latency %d (0=64 .. 7=8192)\n",ctrl.latency); - fprintf(stdout,"ctrl.master %d\n",ctrl.master); - fprintf(stdout,"ctrl.ie %d\n",ctrl.ie); - fprintf(stdout,"ctrl.sr48 %d\n",ctrl.sr48); - fprintf(stdout,"ctrl.spare %d\n",ctrl.spare); - fprintf(stdout,"ctrl.doublespeed %d\n",ctrl.doublespeed); - fprintf(stdout,"ctrl.pro %d\n",ctrl.pro); - fprintf(stdout,"ctrl.emphasis %d\n",ctrl.emphasis); - fprintf(stdout,"ctrl.dolby %d\n",ctrl.dolby); - fprintf(stdout,"ctrl.opt_out %d\n",ctrl.opt_out); - fprintf(stdout,"ctrl.wordclock %d\n",ctrl.wordclock); - fprintf(stdout,"ctrl.spdif_in %d (0=optical,1=coax,2=intern)\n",ctrl.spdif_in); - fprintf(stdout,"ctrl.sync_ref %d (0=ADAT1,1=ADAT2,2=ADAT3,3=SPDIF)\n",ctrl.sync_ref); - fprintf(stdout,"ctrl.spdif_reset %d\n",ctrl.spdif_reset); - fprintf(stdout,"ctrl.spdif_select %d\n",ctrl.spdif_select); - fprintf(stdout,"ctrl.spdif_clock %d\n",ctrl.spdif_clock); - fprintf(stdout,"ctrl.spdif_write %d\n",ctrl.spdif_write); - fprintf(stdout,"ctrl.adat1_cd %d\n",ctrl.adat1_cd); - exit (0); - } - - if (!strcmp(argv[argidx],"mix")) { - rme_mixer mix; - int i; - - for (i=0; i<4; i++) { - mix.devnr = i; - ioctl(fd,SOUND_MIXER_PRIVATE1,&mix); - if (mix.devnr == i) { - fprintf(stdout,"devnr %d\n",mix.devnr); - fprintf(stdout,"mix.i_offset %2d (0-25)\n",mix.i_offset); - fprintf(stdout,"mix.o_offset %2d (0-25)\n",mix.o_offset); - } - } - exit (0); - } - -/* the control flags */ - - if (argc < argidx+2) - usage(); - - if (!strcmp(argv[argidx],"master")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("master = %d\n",val); - ctrl.master = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"pro")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("pro = %d\n",val); - ctrl.pro = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"emphasis")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("emphasis = %d\n",val); - ctrl.emphasis = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"dolby")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("dolby = %d\n",val); - ctrl.dolby = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"optout")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("optout = %d\n",val); - ctrl.opt_out = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"wordclock")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("wordclock = %d\n",val); - ctrl.wordclock = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"spdifin")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("spdifin = %d\n",val); - ctrl.spdif_in = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"syncref")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("syncref = %d\n",val); - ctrl.sync_ref = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - - if (!strcmp(argv[argidx],"adat1cd")) { - int val = atoi(argv[argidx+1]); - ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); - printf("adat1cd = %d\n",val); - ctrl.adat1_cd = val; - ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); - exit (0); - } - -/* setting offset */ - - if (argc < argidx+4) - usage(); - - if (!strcmp(argv[argidx],"offset")) { - rme_mixer mix; - - mix.devnr = atoi(argv[argidx+1]); - - mix.i_offset = atoi(argv[argidx+2]); - mix.o_offset = atoi(argv[argidx+3]); - ioctl(fdwr,SOUND_MIXER_PRIVATE1,&mix); - fprintf(stdout,"devnr %d\n",mix.devnr); - fprintf(stdout,"mix.i_offset to %d\n",mix.i_offset); - fprintf(stdout,"mix.o_offset to %d\n",mix.o_offset); - exit (0); - } - - usage(); - exit (0); /* to avoid warning */ -} - - ----------------------------- -------------------------------- -#!/usr/bin/wish - -# xrmectrl -# (C) 2000 Guenter Geiger -# HP20020201 - Heiko Purnhagen - -#set defaults "-relief ridged" -set CTRLPROG "./rmectrl" -if {$argc} { - set CTRLPROG "$CTRLPROG $argv" -} -puts "CTRLPROG $CTRLPROG" - -frame .butts -button .butts.exit -text "Exit" -command "exit" -relief ridge -#button .butts.state -text "State" -command "get_all" - -pack .butts.exit -side left -pack .butts -side bottom - - -# -# STATUS -# - -frame .status - -# Sampling Rate - -frame .status.sr -label .status.sr.text -text "Sampling Rate" -justify left -radiobutton .status.sr.441 -selectcolor red -text "44.1 kHz" -width 10 -anchor nw -variable srate -value 44100 -font times -radiobutton .status.sr.480 -selectcolor red -text "48 kHz" -width 10 -anchor nw -variable srate -value 48000 -font times -radiobutton .status.sr.882 -selectcolor red -text "88.2 kHz" -width 10 -anchor nw -variable srate -value 88200 -font times -radiobutton .status.sr.960 -selectcolor red -text "96 kHz" -width 10 -anchor nw -variable srate -value 96000 -font times - -pack .status.sr.text .status.sr.441 .status.sr.480 .status.sr.882 .status.sr.960 -side top -padx 3 - -# Lock - -frame .status.lock -label .status.lock.text -text "Lock" -justify left -checkbutton .status.lock.adat1 -selectcolor red -text "ADAT1" -anchor nw -width 10 -variable adatlock1 -font times -checkbutton .status.lock.adat2 -selectcolor red -text "ADAT2" -anchor nw -width 10 -variable adatlock2 -font times -checkbutton .status.lock.adat3 -selectcolor red -text "ADAT3" -anchor nw -width 10 -variable adatlock3 -font times - -pack .status.lock.text .status.lock.adat1 .status.lock.adat2 .status.lock.adat3 -side top -padx 3 - -# Sync - -frame .status.sync -label .status.sync.text -text "Sync" -justify left -checkbutton .status.sync.adat1 -selectcolor red -text "ADAT1" -anchor nw -width 10 -variable adatsync1 -font times -checkbutton .status.sync.adat2 -selectcolor red -text "ADAT2" -anchor nw -width 10 -variable adatsync2 -font times -checkbutton .status.sync.adat3 -selectcolor red -text "ADAT3" -anchor nw -width 10 -variable adatsync3 -font times - -pack .status.sync.text .status.sync.adat1 .status.sync.adat2 .status.sync.adat3 -side top -padx 3 - -# Timecode - -frame .status.tc -label .status.tc.text -text "Timecode" -justify left -checkbutton .status.tc.busy -selectcolor red -text "busy" -anchor nw -width 10 -variable tcbusy -font times -checkbutton .status.tc.out -selectcolor red -text "out" -anchor nw -width 10 -variable tcout -font times -checkbutton .status.tc.valid -selectcolor red -text "valid" -anchor nw -width 10 -variable tcvalid -font times - -pack .status.tc.text .status.tc.busy .status.tc.out .status.tc.valid -side top -padx 3 - -# SPDIF In - -frame .status.spdif -label .status.spdif.text -text "SPDIF In" -justify left -label .status.spdif.sr -text "--.- kHz" -anchor n -width 10 -font times -checkbutton .status.spdif.error -selectcolor red -text "Input Lock" -anchor nw -width 10 -variable spdiferr -font times - -pack .status.spdif.text .status.spdif.sr .status.spdif.error -side top -padx 3 - -pack .status.sr .status.lock .status.sync .status.tc .status.spdif -side left -fill x -anchor n -expand 1 - - -# -# CONTROL -# - -proc setprof {} { - global CTRLPROG - global spprof - exec $CTRLPROG pro $spprof -} - -proc setemph {} { - global CTRLPROG - global spemph - exec $CTRLPROG emphasis $spemph -} - -proc setnoaud {} { - global CTRLPROG - global spnoaud - exec $CTRLPROG dolby $spnoaud -} - -proc setoptical {} { - global CTRLPROG - global spoptical - exec $CTRLPROG optout $spoptical -} - -proc setspdifin {} { - global CTRLPROG - global spdifin - exec $CTRLPROG spdifin [expr $spdifin - 1] -} - -proc setsyncsource {} { - global CTRLPROG - global syncsource - exec $CTRLPROG syncref [expr $syncsource -1] -} - - -proc setmaster {} { - global CTRLPROG - global master - exec $CTRLPROG master $master -} - -proc setwordclock {} { - global CTRLPROG - global wordclock - exec $CTRLPROG wordclock $wordclock -} - -proc setadat1cd {} { - global CTRLPROG - global adat1cd - exec $CTRLPROG adat1cd $adat1cd -} - - -frame .control - -# SPDIF In & SPDIF Out - - -frame .control.spdif - -frame .control.spdif.in -label .control.spdif.in.text -text "SPDIF In" -justify left -radiobutton .control.spdif.in.input1 -text "Optical" -anchor nw -width 13 -variable spdifin -value 1 -command setspdifin -selectcolor blue -font times -radiobutton .control.spdif.in.input2 -text "Coaxial" -anchor nw -width 13 -variable spdifin -value 2 -command setspdifin -selectcolor blue -font times -radiobutton .control.spdif.in.input3 -text "Intern " -anchor nw -width 13 -variable spdifin -command setspdifin -value 3 -selectcolor blue -font times - -checkbutton .control.spdif.in.adat1cd -text "ADAT1 Intern" -anchor nw -width 13 -variable adat1cd -command setadat1cd -selectcolor blue -font times - -pack .control.spdif.in.text .control.spdif.in.input1 .control.spdif.in.input2 .control.spdif.in.input3 .control.spdif.in.adat1cd - -label .control.spdif.space - -frame .control.spdif.out -label .control.spdif.out.text -text "SPDIF Out" -justify left -checkbutton .control.spdif.out.pro -text "Professional" -anchor nw -width 13 -variable spprof -command setprof -selectcolor blue -font times -checkbutton .control.spdif.out.emphasis -text "Emphasis" -anchor nw -width 13 -variable spemph -command setemph -selectcolor blue -font times -checkbutton .control.spdif.out.dolby -text "NoAudio" -anchor nw -width 13 -variable spnoaud -command setnoaud -selectcolor blue -font times -checkbutton .control.spdif.out.optout -text "Optical Out" -anchor nw -width 13 -variable spoptical -command setoptical -selectcolor blue -font times - -pack .control.spdif.out.optout .control.spdif.out.dolby .control.spdif.out.emphasis .control.spdif.out.pro .control.spdif.out.text -side bottom - -pack .control.spdif.in .control.spdif.space .control.spdif.out -side top -fill y -padx 3 -expand 1 - -# Sync Mode & Sync Source - -frame .control.sync -frame .control.sync.mode -label .control.sync.mode.text -text "Sync Mode" -justify left -checkbutton .control.sync.mode.master -text "Master" -anchor nw -width 13 -variable master -command setmaster -selectcolor blue -font times -checkbutton .control.sync.mode.wc -text "Wordclock" -anchor nw -width 13 -variable wordclock -command setwordclock -selectcolor blue -font times - -pack .control.sync.mode.text .control.sync.mode.master .control.sync.mode.wc - -label .control.sync.space - -frame .control.sync.src -label .control.sync.src.text -text "Sync Source" -justify left -radiobutton .control.sync.src.input1 -text "ADAT1" -anchor nw -width 13 -variable syncsource -value 1 -command setsyncsource -selectcolor blue -font times -radiobutton .control.sync.src.input2 -text "ADAT2" -anchor nw -width 13 -variable syncsource -value 2 -command setsyncsource -selectcolor blue -font times -radiobutton .control.sync.src.input3 -text "ADAT3" -anchor nw -width 13 -variable syncsource -command setsyncsource -value 3 -selectcolor blue -font times -radiobutton .control.sync.src.input4 -text "SPDIF" -anchor nw -width 13 -variable syncsource -command setsyncsource -value 4 -selectcolor blue -font times - -pack .control.sync.src.input4 .control.sync.src.input3 .control.sync.src.input2 .control.sync.src.input1 .control.sync.src.text -side bottom - -pack .control.sync.mode .control.sync.space .control.sync.src -side top -fill y -padx 3 -expand 1 - -label .control.space -text "" -width 10 - -# Buffer Size - -frame .control.buf -label .control.buf.text -text "Buffer Size (Latency)" -justify left -radiobutton .control.buf.b1 -selectcolor red -text "64 (1.5 ms)" -width 13 -anchor nw -variable ssrate -value 1 -font times -radiobutton .control.buf.b2 -selectcolor red -text "128 (3 ms)" -width 13 -anchor nw -variable ssrate -value 2 -font times -radiobutton .control.buf.b3 -selectcolor red -text "256 (6 ms)" -width 13 -anchor nw -variable ssrate -value 3 -font times -radiobutton .control.buf.b4 -selectcolor red -text "512 (12 ms)" -width 13 -anchor nw -variable ssrate -value 4 -font times -radiobutton .control.buf.b5 -selectcolor red -text "1024 (23 ms)" -width 13 -anchor nw -variable ssrate -value 5 -font times -radiobutton .control.buf.b6 -selectcolor red -text "2048 (46 ms)" -width 13 -anchor nw -variable ssrate -value 6 -font times -radiobutton .control.buf.b7 -selectcolor red -text "4096 (93 ms)" -width 13 -anchor nw -variable ssrate -value 7 -font times -radiobutton .control.buf.b8 -selectcolor red -text "8192 (186 ms)" -width 13 -anchor nw -variable ssrate -value 8 -font times - -pack .control.buf.text .control.buf.b1 .control.buf.b2 .control.buf.b3 .control.buf.b4 .control.buf.b5 .control.buf.b6 .control.buf.b7 .control.buf.b8 -side top -padx 3 - -# Offset - -frame .control.offset - -frame .control.offset.in -label .control.offset.in.text -text "Offset In" -justify left -label .control.offset.in.off0 -text "dev\#0: -" -anchor nw -width 10 -font times -label .control.offset.in.off1 -text "dev\#1: -" -anchor nw -width 10 -font times -label .control.offset.in.off2 -text "dev\#2: -" -anchor nw -width 10 -font times -label .control.offset.in.off3 -text "dev\#3: -" -anchor nw -width 10 -font times - -pack .control.offset.in.text .control.offset.in.off0 .control.offset.in.off1 .control.offset.in.off2 .control.offset.in.off3 - -label .control.offset.space - -frame .control.offset.out -label .control.offset.out.text -text "Offset Out" -justify left -label .control.offset.out.off0 -text "dev\#0: -" -anchor nw -width 10 -font times -label .control.offset.out.off1 -text "dev\#1: -" -anchor nw -width 10 -font times -label .control.offset.out.off2 -text "dev\#2: -" -anchor nw -width 10 -font times -label .control.offset.out.off3 -text "dev\#3: -" -anchor nw -width 10 -font times - -pack .control.offset.out.off3 .control.offset.out.off2 .control.offset.out.off1 .control.offset.out.off0 .control.offset.out.text -side bottom - -pack .control.offset.in .control.offset.space .control.offset.out -side top -fill y -padx 3 -expand 1 - - -pack .control.spdif .control.sync .control.space .control.buf .control.offset -side left -fill both -anchor n -expand 1 - - -label .statustext -text Status -justify center -relief ridge -label .controltext -text Control -justify center -relief ridge - -label .statusspace -label .controlspace - -pack .statustext .status .statusspace .controltext .control .controlspace -side top -anchor nw -fill both -expand 1 - - -proc get_bit {output sstr} { - set idx1 [string last [concat $sstr 1] $output] - set idx1 [expr $idx1 != -1] - return $idx1 -} - -proc get_val {output sstr} { - set val [string wordend $output [string last $sstr $output]] - set val [string range $output $val [expr $val+1]] - return $val -} - -proc get_val2 {output sstr} { - set val [string wordend $output [string first $sstr $output]] - set val [string range $output $val [expr $val+2]] - return $val -} - -proc get_control {} { - global spprof - global spemph - global spnoaud - global spoptical - global spdifin - global ssrate - global master - global wordclock - global syncsource - global CTRLPROG - - set f [open "| $CTRLPROG control" r+] - set ooo [read $f 1000] - close $f -# puts $ooo - - set spprof [ get_bit $ooo "pro"] - set spemph [ get_bit $ooo "emphasis"] - set spnoaud [ get_bit $ooo "dolby"] - set spoptical [ get_bit $ooo "opt_out"] - set spdifin [ expr [ get_val $ooo "spdif_in"] + 1] - set ssrate [ expr [ get_val $ooo "latency"] + 1] - set master [ expr [ get_val $ooo "master"]] - set wordclock [ expr [ get_val $ooo "wordclock"]] - set syncsource [ expr [ get_val $ooo "sync_ref"] + 1] -} - -proc get_status {} { - global srate - global ctrlcom - - global adatlock1 - global adatlock2 - global adatlock3 - - global adatsync1 - global adatsync2 - global adatsync3 - - global tcbusy - global tcout - global tcvalid - - global spdiferr - global crystal - global .status.spdif.text - global CTRLPROG - - - set f [open "| $CTRLPROG status" r+] - set ooo [read $f 1000] - close $f -# puts $ooo - -# samplerate - - set idx1 [string last "sr48 1" $ooo] - set idx2 [string last "doublespeed 1" $ooo] - if {$idx1 >= 0} { - set fact1 48000 - } else { - set fact1 44100 - } - - if {$idx2 >= 0} { - set fact2 2 - } else { - set fact2 1 - } - set srate [expr $fact1 * $fact2] -# ADAT lock - - set val [get_val $ooo lockmask] - set adatlock1 0 - set adatlock2 0 - set adatlock3 0 - if {[expr $val & 1]} { - set adatlock3 1 - } - if {[expr $val & 2]} { - set adatlock2 1 - } - if {[expr $val & 4]} { - set adatlock1 1 - } - -# ADAT sync - set val [get_val $ooo syncmask] - set adatsync1 0 - set adatsync2 0 - set adatsync3 0 - - if {[expr $val & 1]} { - set adatsync3 1 - } - if {[expr $val & 2]} { - set adatsync2 1 - } - if {[expr $val & 4]} { - set adatsync1 1 - } - -# TC busy - - set tcbusy [get_bit $ooo "busy"] - set tcout [get_bit $ooo "out"] - set tcvalid [get_bit $ooo "valid"] - set spdiferr [expr [get_bit $ooo "spdif_error"] == 0] - -# 000=64kHz, 100=88.2kHz, 011=96kHz -# 111=32kHz, 110=44.1kHz, 101=48kHz - - set val [get_val $ooo crystalrate] - - set crystal "--.- kHz" - if {$val == 0} { - set crystal "64 kHz" - } - if {$val == 4} { - set crystal "88.2 kHz" - } - if {$val == 3} { - set crystal "96 kHz" - } - if {$val == 7} { - set crystal "32 kHz" - } - if {$val == 6} { - set crystal "44.1 kHz" - } - if {$val == 5} { - set crystal "48 kHz" - } - .status.spdif.sr configure -text $crystal -} - -proc get_offset {} { - global inoffset - global outoffset - global CTRLPROG - - set f [open "| $CTRLPROG mix" r+] - set ooo [read $f 1000] - close $f -# puts $ooo - - if { [string match "*devnr*" $ooo] } { - set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] - set val [get_val2 $ooo i_offset] - .control.offset.in.off0 configure -text "dev\#0: $val" - set val [get_val2 $ooo o_offset] - .control.offset.out.off0 configure -text "dev\#0: $val" - } else { - .control.offset.in.off0 configure -text "dev\#0: -" - .control.offset.out.off0 configure -text "dev\#0: -" - } - if { [string match "*devnr*" $ooo] } { - set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] - set val [get_val2 $ooo i_offset] - .control.offset.in.off1 configure -text "dev\#1: $val" - set val [get_val2 $ooo o_offset] - .control.offset.out.off1 configure -text "dev\#1: $val" - } else { - .control.offset.in.off1 configure -text "dev\#1: -" - .control.offset.out.off1 configure -text "dev\#1: -" - } - if { [string match "*devnr*" $ooo] } { - set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] - set val [get_val2 $ooo i_offset] - .control.offset.in.off2 configure -text "dev\#2: $val" - set val [get_val2 $ooo o_offset] - .control.offset.out.off2 configure -text "dev\#2: $val" - } else { - .control.offset.in.off2 configure -text "dev\#2: -" - .control.offset.out.off2 configure -text "dev\#2: -" - } - if { [string match "*devnr*" $ooo] } { - set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] - set val [get_val2 $ooo i_offset] - .control.offset.in.off3 configure -text "dev\#3: $val" - set val [get_val2 $ooo o_offset] - .control.offset.out.off3 configure -text "dev\#3: $val" - } else { - .control.offset.in.off3 configure -text "dev\#3: -" - .control.offset.out.off3 configure -text "dev\#3: -" - } -} - - -proc get_all {} { -get_status -get_control -get_offset -} - -# main -while {1} { - after 200 - get_all - update -} diff --git a/Documentation/sound/oss/solo1 b/Documentation/sound/oss/solo1 deleted file mode 100644 index 95c4c83422..0000000000 --- a/Documentation/sound/oss/solo1 +++ /dev/null @@ -1,70 +0,0 @@ -Recording ---------- - -Recording does not work on the author's card, but there -is at least one report of it working on later silicon. -The chip behaves differently than described in the data sheet, -likely due to a chip bug. Working around this would require -the help of ESS (for example by publishing an errata sheet), -but ESS has not done so far. - -Also, the chip only supports 24 bit addresses for recording, -which means it cannot work on some Alpha mainboards. - - -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (or later, available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -The card has an OPL compatible FM synthesizer. - -Thomas Sailer -t.sailer@alumni.ethz.ch diff --git a/Documentation/sound/oss/sonicvibes b/Documentation/sound/oss/sonicvibes deleted file mode 100644 index 84dee2e0b3..0000000000 --- a/Documentation/sound/oss/sonicvibes +++ /dev/null @@ -1,81 +0,0 @@ -/proc/sound, /dev/sndstat -------------------------- - -/proc/sound and /dev/sndstat is not supported by the -driver. To find out whether the driver succeeded loading, -check the kernel log (dmesg). - - -ALaw/uLaw sample formats ------------------------- - -This driver does not support the ALaw/uLaw sample formats. -ALaw is the default mode when opening a sound device -using OSS/Free. The reason for the lack of support is -that the hardware does not support these formats, and adding -conversion routines to the kernel would lead to very ugly -code in the presence of the mmap interface to the driver. -And since xquake uses mmap, mmap is considered important :-) -and no sane application uses ALaw/uLaw these days anyway. -In short, playing a Sun .au file as follows: - -cat my_file.au > /dev/dsp - -does not work. Instead, you may use the play script from -Chris Bagwell's sox-12.14 package (available from the URL -below) to play many different audio file formats. -The script automatically determines the audio format -and does do audio conversions if necessary. -http://home.sprynet.com/sprynet/cbagwell/projects.html - - -Blocking vs. nonblocking IO ---------------------------- - -Unlike OSS/Free this driver honours the O_NONBLOCK file flag -not only during open, but also during read and write. -This is an effort to make the sound driver interface more -regular. Timidity has problems with this; a patch -is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. -(Timidity patched will also run on OSS/Free). - - -MIDI UART ---------- - -The driver supports a simple MIDI UART interface, with -no ioctl's supported. - - -MIDI synthesizer ----------------- - -The card both has an OPL compatible FM synthesizer as well as -a wavetable synthesizer. - -I haven't managed so far to get the OPL synth running. - -Using the wavetable synthesizer requires allocating -1-4MB of physically contiguous memory, which isn't possible -currently on Linux without ugly hacks like the bigphysarea -patch. Therefore, the driver doesn't support wavetable -synthesis. - - -No support from S3 ------------------- - -I do not get any support from S3. Therefore, the driver -still has many problems. For example, although the manual -states that the chip should be able to access the sample -buffer anywhere in 32bit address space, I haven't managed to -get it working with buffers above 16M. Therefore, the card -has the same disadvantages as ISA soundcards. - -Given that the card is also very noisy, and if you haven't -already bought it, you should strongly opt for one of the -comparatively priced Ensoniq products. - - -Thomas Sailer -t.sailer@alumni.ethz.ch diff --git a/MAINTAINERS b/MAINTAINERS index 1c6223d3ce..8c35b3c503 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -977,6 +977,13 @@ L: ebtables-devel@lists.sourceforge.net W: http://ebtables.sourceforge.net/ S: Maintained +ECRYPT FILE SYSTEM +P: Mike Halcrow, Phillip Hellewell +M: mhalcrow@us.ibm.com, phillip@hellewell.homeip.net +L: ecryptfs-devel@lists.sourceforge.net +W: http://ecryptfs.sourceforge.net/ +S: Supported + EDAC-CORE P: Doug Thompson M: norsk5@xmission.com @@ -1893,11 +1900,6 @@ M: rroesler@syskonnect.de W: http://www.syskonnect.com S: Supported -MAESTRO PCI SOUND DRIVERS -P: Zach Brown -M: zab@zabbo.net -S: Odd Fixes - MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 P: Michael Kerrisk M: mtk-manpages@gmx.net @@ -2434,6 +2436,19 @@ M: mporter@kernel.crashing.org L: linux-kernel@vger.kernel.org S: Maintained +READ-COPY UPDATE (RCU) +P: Dipankar Sarma +M: dipankar@in.ibm.com +W: http://www.rdrop.com/users/paulmck/rclock/ +L: linux-kernel@vger.kernel.org +S: Supported + +RCUTORTURE MODULE +P: Josh Triplett +M: josh@freedesktop.org +L: linux-kernel@vger.kernel.org +S: Maintained + REAL TIME CLOCK DRIVER P: Paul Gortmaker M: p_gortmaker@yahoo.com @@ -2854,6 +2869,11 @@ M: hlhung3i@gmail.com W: http://tcp-lp-mod.sourceforge.net/ S: Maintained +TI FLASH MEDIA INTERFACE DRIVER +P: Alex Dubov +M: oakad@yahoo.com +S: Maintained + TI OMAP RANDOM NUMBER GENERATOR SUPPORT P: Deepak Saxena M: dsaxena@plexity.net @@ -3377,12 +3397,6 @@ M: Henk.Vergonet@gmail.com L: usbb2k-api-dev@nongnu.org S: Maintained -YMFPCI YAMAHA PCI SOUND (Use ALSA instead) -P: Pete Zaitcev -M: zaitcev@yahoo.com -L: linux-kernel@vger.kernel.org -S: Obsolete - Z8530 DRIVER FOR AX.25 P: Joerg Reuter M: jreuter@yaina.de diff --git a/Makefile b/Makefile index 4c6c5e32ef..adb2c748e1 100644 --- a/Makefile +++ b/Makefile @@ -1321,7 +1321,7 @@ define xtags --langdef=kconfig \ --language-force=kconfig \ --regex-kconfig='/^[[:blank:]]*config[[:blank:]]+([[:alnum:]_]+)/\1/'; \ - $(all-defconfigs) | xargs $1 -a \ + $(all-defconfigs) | xargs -r $1 -a \ --langdef=dotconfig \ --language-force=dotconfig \ --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/'; \ @@ -1329,7 +1329,7 @@ define xtags $(all-sources) | xargs $1 -a; \ $(all-kconfigs) | xargs $1 -a \ --regex='/^[ \t]*config[ \t]+\([a-zA-Z0-9_]+\)/\1/'; \ - $(all-defconfigs) | xargs $1 -a \ + $(all-defconfigs) | xargs -r $1 -a \ --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/'; \ else \ $(all-sources) | xargs $1 -a; \ diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index 1aaea6ab8c..92f79cdd9a 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -62,8 +62,6 @@ static inline int acpi_madt_oem_check(char *oem_id, char *oem_table_id) { return #include #endif /* CONFIG_X86_LOCAL_APIC */ -static inline int gsi_irq_sharing(int gsi) { return gsi; } - #endif /* X86 */ #define BAD_MADT_ENTRY(entry, end) ( \ @@ -468,12 +466,7 @@ void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger) int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) { -#ifdef CONFIG_X86_IO_APIC - if (use_pci_vector() && !platform_legacy_irq(gsi)) - *irq = IO_APIC_VECTOR(gsi); - else -#endif - *irq = gsi_irq_sharing(gsi); + *irq = gsi; return 0; } diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index ea5f4e7958..d07ed31f11 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -34,35 +34,15 @@ * moves to arch independent land */ -DEFINE_SPINLOCK(i8259A_lock); - -static void end_8259A_irq (unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && - irq_desc[irq].action) - enable_8259A_irq(irq); -} - -#define shutdown_8259A_irq disable_8259A_irq - static int i8259A_auto_eoi; - +DEFINE_SPINLOCK(i8259A_lock); static void mask_and_ack_8259A(unsigned int); -unsigned int startup_8259A_irq(unsigned int irq) -{ - enable_8259A_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type i8259A_irq_type = { - .typename = "XT-PIC", - .startup = startup_8259A_irq, - .shutdown = shutdown_8259A_irq, - .enable = enable_8259A_irq, - .disable = disable_8259A_irq, - .ack = mask_and_ack_8259A, - .end = end_8259A_irq, +static struct irq_chip i8259A_chip = { + .name = "XT-PIC", + .mask = disable_8259A_irq, + .unmask = enable_8259A_irq, + .mask_ack = mask_and_ack_8259A, }; /* @@ -133,7 +113,7 @@ void make_8259A_irq(unsigned int irq) { disable_irq_nosync(irq); io_apic_irqs &= ~(1< #include #include +#include +#include +#include #include #include @@ -38,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -86,15 +91,6 @@ static struct irq_pin_list { int apic, pin, next; } irq_2_pin[PIN_MAP_SIZE]; -int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1}; -#ifdef CONFIG_PCI_MSI -#define vector_to_irq(vector) \ - (platform_legacy_irq(vector) ? vector : vector_irq[vector]) -#else -#define vector_to_irq(vector) (vector) -#endif - - union entry_union { struct { u32 w1, w2; }; struct IO_APIC_route_entry entry; @@ -280,7 +276,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t cpumask) break; entry = irq_2_pin + entry->next; } - set_irq_info(irq, cpumask); + set_native_irq_info(irq, cpumask); spin_unlock_irqrestore(&ioapic_lock, flags); } @@ -1181,46 +1177,45 @@ static inline int IO_APIC_irq_trigger(int irq) /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 }; -int assign_irq_vector(int irq) +static int __assign_irq_vector(int irq) { static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; - unsigned long flags; int vector; - BUG_ON(irq != AUTO_ASSIGN && (unsigned)irq >= NR_IRQ_VECTORS); + BUG_ON((unsigned)irq >= NR_IRQ_VECTORS); - spin_lock_irqsave(&vector_lock, flags); - - if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0) { - spin_unlock_irqrestore(&vector_lock, flags); + if (IO_APIC_VECTOR(irq) > 0) return IO_APIC_VECTOR(irq); - } -next: + current_vector += 8; if (current_vector == SYSCALL_VECTOR) - goto next; + current_vector += 8; if (current_vector >= FIRST_SYSTEM_VECTOR) { offset++; - if (!(offset%8)) { - spin_unlock_irqrestore(&vector_lock, flags); + if (!(offset % 8)) return -ENOSPC; - } current_vector = FIRST_DEVICE_VECTOR + offset; } vector = current_vector; - vector_irq[vector] = irq; - if (irq != AUTO_ASSIGN) - IO_APIC_VECTOR(irq) = vector; + IO_APIC_VECTOR(irq) = vector; + + return vector; +} + +static int assign_irq_vector(int irq) +{ + unsigned long flags; + int vector; + spin_lock_irqsave(&vector_lock, flags); + vector = __assign_irq_vector(irq); spin_unlock_irqrestore(&vector_lock, flags); return vector; } - -static struct hw_interrupt_type ioapic_level_type; -static struct hw_interrupt_type ioapic_edge_type; +static struct irq_chip ioapic_chip; #define IOAPIC_AUTO -1 #define IOAPIC_EDGE 0 @@ -1228,16 +1223,14 @@ static struct hw_interrupt_type ioapic_edge_type; static void ioapic_register_intr(int irq, int vector, unsigned long trigger) { - unsigned idx; - - idx = use_pci_vector() && !platform_legacy_irq(irq) ? vector : irq; - if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || trigger == IOAPIC_LEVEL) - irq_desc[idx].chip = &ioapic_level_type; + set_irq_chip_and_handler(irq, &ioapic_chip, + handle_fasteoi_irq); else - irq_desc[idx].chip = &ioapic_edge_type; - set_intr_gate(vector, interrupt[idx]); + set_irq_chip_and_handler(irq, &ioapic_chip, + handle_edge_irq); + set_intr_gate(vector, interrupt[irq]); } static void __init setup_IO_APIC_irqs(void) @@ -1346,7 +1339,8 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in * The timer IRQ doesn't have to know that behind the * scene we have a 8259A-master in AEOI mode ... */ - irq_desc[0].chip = &ioapic_edge_type; + irq_desc[0].chip = &ioapic_chip; + set_irq_handler(0, handle_edge_irq); /* * Add it to the IO-APIC irq-routing table: @@ -1481,17 +1475,12 @@ void __init print_IO_APIC(void) ); } } - if (use_pci_vector()) - printk(KERN_INFO "Using vector-based indexing\n"); printk(KERN_DEBUG "IRQ to pin mappings:\n"); for (i = 0; i < NR_IRQS; i++) { struct irq_pin_list *entry = irq_2_pin + i; if (entry->pin < 0) continue; - if (use_pci_vector() && !platform_legacy_irq(i)) - printk(KERN_DEBUG "IRQ%d ", IO_APIC_VECTOR(i)); - else - printk(KERN_DEBUG "IRQ%d ", i); + printk(KERN_DEBUG "IRQ%d ", i); for (;;) { printk("-> %d:%d", entry->apic, entry->pin); if (!entry->next) @@ -1918,6 +1907,8 @@ static int __init timer_irq_works(void) */ /* + * Startup quirk: + * * Starting up a edge-triggered IO-APIC interrupt is * nasty - we need to make sure that we get the edge. * If it is already asserted for some reason, we need @@ -1925,8 +1916,10 @@ static int __init timer_irq_works(void) * * This is not complete - we should be able to fake * an edge even if it isn't on the 8259A... + * + * (We do this for level-triggered IRQs too - it cannot hurt.) */ -static unsigned int startup_edge_ioapic_irq(unsigned int irq) +static unsigned int startup_ioapic_irq(unsigned int irq) { int was_pending = 0; unsigned long flags; @@ -1943,47 +1936,18 @@ static unsigned int startup_edge_ioapic_irq(unsigned int irq) return was_pending; } -/* - * Once we have recorded IRQ_PENDING already, we can mask the - * interrupt for real. This prevents IRQ storms from unhandled - * devices. - */ -static void ack_edge_ioapic_irq(unsigned int irq) +static void ack_ioapic_irq(unsigned int irq) { - move_irq(irq); - if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) - == (IRQ_PENDING | IRQ_DISABLED)) - mask_IO_APIC_irq(irq); + move_native_irq(irq); ack_APIC_irq(); } -/* - * Level triggered interrupts can just be masked, - * and shutting down and starting up the interrupt - * is the same as enabling and disabling them -- except - * with a startup need to return a "was pending" value. - * - * Level triggered interrupts are special because we - * do not touch any IO-APIC register while handling - * them. We ack the APIC in the end-IRQ handler, not - * in the start-IRQ-handler. Protection against reentrance - * from the same interrupt is still provided, both by the - * generic IRQ layer and by the fact that an unacked local - * APIC does not accept IRQs. - */ -static unsigned int startup_level_ioapic_irq (unsigned int irq) -{ - unmask_IO_APIC_irq(irq); - - return 0; /* don't check for pending */ -} - -static void end_level_ioapic_irq (unsigned int irq) +static void ack_ioapic_quirk_irq(unsigned int irq) { unsigned long v; int i; - move_irq(irq); + move_native_irq(irq); /* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various @@ -2018,105 +1982,26 @@ static void end_level_ioapic_irq (unsigned int irq) } } -#ifdef CONFIG_PCI_MSI -static unsigned int startup_edge_ioapic_vector(unsigned int vector) -{ - int irq = vector_to_irq(vector); - - return startup_edge_ioapic_irq(irq); -} - -static void ack_edge_ioapic_vector(unsigned int vector) -{ - int irq = vector_to_irq(vector); - - move_native_irq(vector); - ack_edge_ioapic_irq(irq); -} - -static unsigned int startup_level_ioapic_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - return startup_level_ioapic_irq (irq); -} - -static void end_level_ioapic_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - move_native_irq(vector); - end_level_ioapic_irq(irq); -} - -static void mask_IO_APIC_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - mask_IO_APIC_irq(irq); -} - -static void unmask_IO_APIC_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - unmask_IO_APIC_irq(irq); -} - -#ifdef CONFIG_SMP -static void set_ioapic_affinity_vector (unsigned int vector, - cpumask_t cpu_mask) -{ - int irq = vector_to_irq(vector); - - set_native_irq_info(vector, cpu_mask); - set_ioapic_affinity_irq(irq, cpu_mask); -} -#endif -#endif - -static int ioapic_retrigger(unsigned int irq) +static int ioapic_retrigger_irq(unsigned int irq) { send_IPI_self(IO_APIC_VECTOR(irq)); return 1; } -/* - * Level and edge triggered IO-APIC interrupts need different handling, - * so we use two separate IRQ descriptors. Edge triggered IRQs can be - * handled with the level-triggered descriptor, but that one has slightly - * more overhead. Level-triggered interrupts cannot be handled with the - * edge-triggered handler, without risking IRQ storms and other ugly - * races. - */ -static struct hw_interrupt_type ioapic_edge_type __read_mostly = { - .typename = "IO-APIC-edge", - .startup = startup_edge_ioapic, - .shutdown = shutdown_edge_ioapic, - .enable = enable_edge_ioapic, - .disable = disable_edge_ioapic, - .ack = ack_edge_ioapic, - .end = end_edge_ioapic, +static struct irq_chip ioapic_chip __read_mostly = { + .name = "IO-APIC", + .startup = startup_ioapic_irq, + .mask = mask_IO_APIC_irq, + .unmask = unmask_IO_APIC_irq, + .ack = ack_ioapic_irq, + .eoi = ack_ioapic_quirk_irq, #ifdef CONFIG_SMP - .set_affinity = set_ioapic_affinity, + .set_affinity = set_ioapic_affinity_irq, #endif - .retrigger = ioapic_retrigger, + .retrigger = ioapic_retrigger_irq, }; -static struct hw_interrupt_type ioapic_level_type __read_mostly = { - .typename = "IO-APIC-level", - .startup = startup_level_ioapic, - .shutdown = shutdown_level_ioapic, - .enable = enable_level_ioapic, - .disable = disable_level_ioapic, - .ack = mask_and_ack_level_ioapic, - .end = end_level_ioapic, -#ifdef CONFIG_SMP - .set_affinity = set_ioapic_affinity, -#endif - .retrigger = ioapic_retrigger, -}; static inline void init_IO_APIC_traps(void) { @@ -2135,11 +2020,6 @@ static inline void init_IO_APIC_traps(void) */ for (irq = 0; irq < NR_IRQS ; irq++) { int tmp = irq; - if (use_pci_vector()) { - if (!platform_legacy_irq(tmp)) - if ((tmp = vector_to_irq(tmp)) == -1) - continue; - } if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) { /* * Hmm.. We don't have an entry for this, @@ -2150,20 +2030,21 @@ static inline void init_IO_APIC_traps(void) make_8259A_irq(irq); else /* Strange. Oh, well.. */ - irq_desc[irq].chip = &no_irq_type; + irq_desc[irq].chip = &no_irq_chip; } } } -static void enable_lapic_irq (unsigned int irq) -{ - unsigned long v; +/* + * The local APIC irq-chip implementation: + */ - v = apic_read(APIC_LVT0); - apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); +static void ack_apic(unsigned int irq) +{ + ack_APIC_irq(); } -static void disable_lapic_irq (unsigned int irq) +static void mask_lapic_irq (unsigned int irq) { unsigned long v; @@ -2171,21 +2052,19 @@ static void disable_lapic_irq (unsigned int irq) apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); } -static void ack_lapic_irq (unsigned int irq) +static void unmask_lapic_irq (unsigned int irq) { - ack_APIC_irq(); -} + unsigned long v; -static void end_lapic_irq (unsigned int i) { /* nothing */ } + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); +} -static struct hw_interrupt_type lapic_irq_type __read_mostly = { - .typename = "local-APIC-edge", - .startup = NULL, /* startup_irq() not used for IRQ0 */ - .shutdown = NULL, /* shutdown_irq() not used for IRQ0 */ - .enable = enable_lapic_irq, - .disable = disable_lapic_irq, - .ack = ack_lapic_irq, - .end = end_lapic_irq +static struct irq_chip lapic_chip __read_mostly = { + .name = "local-APIC-edge", + .mask = mask_lapic_irq, + .unmask = unmask_lapic_irq, + .eoi = ack_apic, }; static void setup_nmi (void) @@ -2356,7 +2235,7 @@ static inline void check_timer(void) printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); disable_8259A_irq(0); - irq_desc[0].chip = &lapic_irq_type; + set_irq_chip_and_handler(0, &lapic_chip, handle_fasteoi_irq); apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ enable_8259A_irq(0); @@ -2531,6 +2410,238 @@ static int __init ioapic_init_sysfs(void) device_initcall(ioapic_init_sysfs); +/* + * Dynamic irq allocate and deallocation + */ +int create_irq(void) +{ + /* Allocate an unused irq */ + int irq, new, vector; + unsigned long flags; + + irq = -ENOSPC; + spin_lock_irqsave(&vector_lock, flags); + for (new = (NR_IRQS - 1); new >= 0; new--) { + if (platform_legacy_irq(new)) + continue; + if (irq_vector[new] != 0) + continue; + vector = __assign_irq_vector(new); + if (likely(vector > 0)) + irq = new; + break; + } + spin_unlock_irqrestore(&vector_lock, flags); + + if (irq >= 0) { + set_intr_gate(vector, interrupt[irq]); + dynamic_irq_init(irq); + } + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + spin_lock_irqsave(&vector_lock, flags); + irq_vector[irq] = 0; + spin_unlock_irqrestore(&vector_lock, flags); +} + +/* + * MSI mesage composition + */ +#ifdef CONFIG_PCI_MSI +static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg) +{ + int vector; + unsigned dest; + + vector = assign_irq_vector(irq); + if (vector >= 0) { + dest = cpu_mask_to_apicid(TARGET_CPUS); + + msg->address_hi = MSI_ADDR_BASE_HI; + msg->address_lo = + MSI_ADDR_BASE_LO | + ((INT_DEST_MODE == 0) ? + MSI_ADDR_DEST_MODE_PHYSICAL: + MSI_ADDR_DEST_MODE_LOGICAL) | + ((INT_DELIVERY_MODE != dest_LowestPrio) ? + MSI_ADDR_REDIRECTION_CPU: + MSI_ADDR_REDIRECTION_LOWPRI) | + MSI_ADDR_DEST_ID(dest); + + msg->data = + MSI_DATA_TRIGGER_EDGE | + MSI_DATA_LEVEL_ASSERT | + ((INT_DELIVERY_MODE != dest_LowestPrio) ? + MSI_DATA_DELIVERY_FIXED: + MSI_DATA_DELIVERY_LOWPRI) | + MSI_DATA_VECTOR(vector); + } + return vector; +} + +#ifdef CONFIG_SMP +static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) +{ + struct msi_msg msg; + unsigned int dest; + cpumask_t tmp; + int vector; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + tmp = TARGET_CPUS; + + vector = assign_irq_vector(irq); + if (vector < 0) + return; + + dest = cpu_mask_to_apicid(mask); + + read_msi_msg(irq, &msg); + + msg.data &= ~MSI_DATA_VECTOR_MASK; + msg.data |= MSI_DATA_VECTOR(vector); + msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; + msg.address_lo |= MSI_ADDR_DEST_ID(dest); + + write_msi_msg(irq, &msg); + set_native_irq_info(irq, mask); +} +#endif /* CONFIG_SMP */ + +/* + * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, + * which implement the MSI or MSI-X Capability Structure. + */ +static struct irq_chip msi_chip = { + .name = "PCI-MSI", + .unmask = unmask_msi_irq, + .mask = mask_msi_irq, + .ack = ack_ioapic_irq, +#ifdef CONFIG_SMP + .set_affinity = set_msi_irq_affinity, +#endif + .retrigger = ioapic_retrigger_irq, +}; + +int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev) +{ + struct msi_msg msg; + int ret; + ret = msi_compose_msg(dev, irq, &msg); + if (ret < 0) + return ret; + + write_msi_msg(irq, &msg); + + set_irq_chip_and_handler(irq, &msi_chip, handle_edge_irq); + + return 0; +} + +void arch_teardown_msi_irq(unsigned int irq) +{ + return; +} + +#endif /* CONFIG_PCI_MSI */ + +/* + * Hypertransport interrupt support + */ +#ifdef CONFIG_HT_IRQ + +#ifdef CONFIG_SMP + +static void target_ht_irq(unsigned int irq, unsigned int dest) +{ + u32 low, high; + low = read_ht_irq_low(irq); + high = read_ht_irq_high(irq); + + low &= ~(HT_IRQ_LOW_DEST_ID_MASK); + high &= ~(HT_IRQ_HIGH_DEST_ID_MASK); + + low |= HT_IRQ_LOW_DEST_ID(dest); + high |= HT_IRQ_HIGH_DEST_ID(dest); + + write_ht_irq_low(irq, low); + write_ht_irq_high(irq, high); +} + +static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) +{ + unsigned int dest; + cpumask_t tmp; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + tmp = TARGET_CPUS; + + cpus_and(mask, tmp, CPU_MASK_ALL); + + dest = cpu_mask_to_apicid(mask); + + target_ht_irq(irq, dest); + set_native_irq_info(irq, mask); +} +#endif + +static struct hw_interrupt_type ht_irq_chip = { + .name = "PCI-HT", + .mask = mask_ht_irq, + .unmask = unmask_ht_irq, + .ack = ack_ioapic_irq, +#ifdef CONFIG_SMP + .set_affinity = set_ht_irq_affinity, +#endif + .retrigger = ioapic_retrigger_irq, +}; + +int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) +{ + int vector; + + vector = assign_irq_vector(irq); + if (vector >= 0) { + u32 low, high; + unsigned dest; + cpumask_t tmp; + + cpus_clear(tmp); + cpu_set(vector >> 8, tmp); + dest = cpu_mask_to_apicid(tmp); + + high = HT_IRQ_HIGH_DEST_ID(dest); + + low = HT_IRQ_LOW_BASE | + HT_IRQ_LOW_DEST_ID(dest) | + HT_IRQ_LOW_VECTOR(vector) | + ((INT_DEST_MODE == 0) ? + HT_IRQ_LOW_DM_PHYSICAL : + HT_IRQ_LOW_DM_LOGICAL) | + HT_IRQ_LOW_RQEOI_EDGE | + ((INT_DELIVERY_MODE != dest_LowestPrio) ? + HT_IRQ_LOW_MT_FIXED : + HT_IRQ_LOW_MT_ARBITRATED) | + HT_IRQ_LOW_IRQ_MASKED; + + write_ht_irq_low(irq, low); + write_ht_irq_high(irq, high); + + set_irq_chip_and_handler(irq, &ht_irq_chip, handle_edge_irq); + } + return vector; +} +#endif /* CONFIG_HT_IRQ */ + /* -------------------------------------------------------------------------- ACPI-based IOAPIC Configuration -------------------------------------------------------------------------- */ @@ -2684,7 +2795,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a ioapic_write_entry(ioapic, pin, entry); spin_lock_irqsave(&ioapic_lock, flags); - set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); + set_native_irq_info(irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); return 0; diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 5fe547cd8f..3dd2e18015 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -55,6 +55,7 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs) { /* high bit used in ret_from_ code */ int irq = ~regs->orig_eax; + struct irq_desc *desc = irq_desc + irq; #ifdef CONFIG_4KSTACKS union irq_ctx *curctx, *irqctx; u32 *isp; @@ -94,7 +95,7 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs) * current stack (which is the irq stack already after all) */ if (curctx != irqctx) { - int arg1, arg2, ebx; + int arg1, arg2, arg3, ebx; /* build the stack frame on the IRQ stack */ isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); @@ -110,16 +111,17 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs) (curctx->tinfo.preempt_count & SOFTIRQ_MASK); asm volatile( - " xchgl %%ebx,%%esp \n" - " call __do_IRQ \n" + " xchgl %%ebx,%%esp \n" + " call *%%edi \n" " movl %%ebx,%%esp \n" - : "=a" (arg1), "=d" (arg2), "=b" (ebx) - : "0" (irq), "1" (regs), "2" (isp) - : "memory", "cc", "ecx" + : "=a" (arg1), "=d" (arg2), "=c" (arg3), "=b" (ebx) + : "0" (irq), "1" (desc), "2" (regs), "3" (isp), + "D" (desc->handle_irq) + : "memory", "cc" ); } else #endif - __do_IRQ(irq, regs); + desc->handle_irq(irq, desc, regs); irq_exit(); @@ -253,7 +255,8 @@ int show_interrupts(struct seq_file *p, void *v) for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif - seq_printf(p, " %14s", irq_desc[i].chip->typename); + seq_printf(p, " %8s", irq_desc[i].chip->name); + seq_printf(p, "-%s", handle_irq_name(irq_desc[i].handle_irq)); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) diff --git a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c index 4a8995c9c7..47f02af74b 100644 --- a/arch/i386/pci/irq.c +++ b/arch/i386/pci/irq.c @@ -981,10 +981,6 @@ static void __init pcibios_fixup_irqs(void) pci_name(bridge), 'A' + pin, irq); } if (irq >= 0) { - if (use_pci_vector() && - !platform_legacy_irq(irq)) - irq = IO_APIC_VECTOR(irq); - printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n", pci_name(dev), 'A' + pin, irq); dev->irq = irq; @@ -1169,33 +1165,3 @@ static int pirq_enable_irq(struct pci_dev *dev) } return 0; } - -int pci_vector_resources(int last, int nr_released) -{ - int count = nr_released; - - int next = last; - int offset = (last % 8); - - while (next < FIRST_SYSTEM_VECTOR) { - next += 8; -#ifdef CONFIG_X86_64 - if (next == IA32_SYSCALL_VECTOR) - continue; -#else - if (next == SYSCALL_VECTOR) - continue; -#endif - count++; - if (next >= FIRST_SYSTEM_VECTOR) { - if (offset%8) { - next = FIRST_DEVICE_VECTOR + offset; - offset++; - continue; - } - count--; - } - } - - return count; -} diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 31497496eb..cfa099b04c 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o obj-$(CONFIG_AUDIT) += audit.o +obj-$(CONFIG_PCI_MSI) += msi_ia64.o mca_recovery-y += mca_drv.o mca_drv_asm.o obj-$(CONFIG_IA64_ESI) += esi.o diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c index aafca18ab3..ab2d19c366 100644 --- a/arch/ia64/kernel/irq_ia64.c +++ b/arch/ia64/kernel/irq_ia64.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,25 @@ reserve_irq_vector (int vector) return test_and_set_bit(pos, ia64_vector_mask); } +/* + * Dynamic irq allocate and deallocation for MSI + */ +int create_irq(void) +{ + int vector = assign_irq_vector(AUTO_ASSIGN); + + if (vector >= 0) + dynamic_irq_init(vector); + + return vector; +} + +void destroy_irq(unsigned int irq) +{ + dynamic_irq_cleanup(irq); + free_irq_vector(irq); +} + #ifdef CONFIG_SMP # define IS_RESCHEDULE(vec) (vec == IA64_IPI_RESCHEDULE) #else diff --git a/arch/ia64/kernel/msi_ia64.c b/arch/ia64/kernel/msi_ia64.c new file mode 100644 index 0000000000..822e59a1b8 --- /dev/null +++ b/arch/ia64/kernel/msi_ia64.c @@ -0,0 +1,143 @@ +/* + * MSI hooks for standard x86 apic + */ + +#include +#include +#include +#include + +/* + * Shifts for APIC-based data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) + +#define MSI_DATA_DELIVERY_SHIFT 8 +#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) +#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) + +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) +#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) + +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) +#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) + +/* + * Shift/mask fields for APIC-based bus address + */ + +#define MSI_TARGET_CPU_SHIFT 4 +#define MSI_ADDR_HEADER 0xfee00000 + +#define MSI_ADDR_DESTID_MASK 0xfff0000f +#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) + +#define MSI_ADDR_DESTMODE_SHIFT 2 +#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) +#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) + +#define MSI_ADDR_REDIRECTION_SHIFT 3 +#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) +#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) + +static struct irq_chip ia64_msi_chip; + +#ifdef CONFIG_SMP +static void ia64_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) +{ + struct msi_msg msg; + u32 addr; + + read_msi_msg(irq, &msg); + + addr = msg.address_lo; + addr &= MSI_ADDR_DESTID_MASK; + addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(first_cpu(cpu_mask))); + msg.address_lo = addr; + + write_msi_msg(irq, &msg); + set_native_irq_info(irq, cpu_mask); +} +#endif /* CONFIG_SMP */ + +int ia64_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) +{ + struct msi_msg msg; + unsigned long dest_phys_id; + unsigned int vector; + + dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); + vector = irq; + + msg.address_hi = 0; + msg.address_lo = + MSI_ADDR_HEADER | + MSI_ADDR_DESTMODE_PHYS | + MSI_ADDR_REDIRECTION_CPU | + MSI_ADDR_DESTID_CPU(dest_phys_id); + + msg.data = + MSI_DATA_TRIGGER_EDGE | + MSI_DATA_LEVEL_ASSERT | + MSI_DATA_DELIVERY_FIXED | + MSI_DATA_VECTOR(vector); + + write_msi_msg(irq, &msg); + set_irq_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq); + + return 0; +} + +void ia64_teardown_msi_irq(unsigned int irq) +{ + return; /* no-op */ +} + +static void ia64_ack_msi_irq(unsigned int irq) +{ + move_native_irq(irq); + ia64_eoi(); +} + +static int ia64_msi_retrigger_irq(unsigned int irq) +{ + unsigned int vector = irq; + ia64_resend_irq(vector); + + return 1; +} + +/* + * Generic ops used on most IA64 platforms. + */ +static struct irq_chip ia64_msi_chip = { + .name = "PCI-MSI", + .mask = mask_msi_irq, + .unmask = unmask_msi_irq, + .ack = ia64_ack_msi_irq, +#ifdef CONFIG_SMP + .set_affinity = ia64_set_msi_irq_affinity, +#endif + .retrigger = ia64_msi_retrigger_irq, +}; + + +int arch_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) +{ + if (platform_setup_msi_irq) + return platform_setup_msi_irq(irq, pdev); + + return ia64_setup_msi_irq(irq, pdev); +} + +void arch_teardown_msi_irq(unsigned int irq) +{ + if (platform_teardown_msi_irq) + return platform_teardown_msi_irq(irq); + + return ia64_teardown_msi_irq(irq); +} diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 15c7c670da..b30be7c48b 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -810,12 +810,3 @@ pcibios_prep_mwi (struct pci_dev *dev) } return rc; } - -int pci_vector_resources(int last, int nr_released) -{ - int count = nr_released; - - count += (IA64_LAST_DEVICE_VECTOR - last); - - return count; -} diff --git a/arch/ia64/sn/kernel/Makefile b/arch/ia64/sn/kernel/Makefile index ab9c48c880..2d78f34dd7 100644 --- a/arch/ia64/sn/kernel/Makefile +++ b/arch/ia64/sn/kernel/Makefile @@ -19,3 +19,4 @@ xp-y := xp_main.o xp_nofault.o obj-$(CONFIG_IA64_SGI_SN_XP) += xpc.o xpc-y := xpc_main.o xpc_channel.o xpc_partition.o obj-$(CONFIG_IA64_SGI_SN_XP) += xpnet.o +obj-$(CONFIG_PCI_MSI) += msi_sn.o diff --git a/drivers/pci/msi-altix.c b/arch/ia64/sn/kernel/msi_sn.c similarity index 65% rename from drivers/pci/msi-altix.c rename to arch/ia64/sn/kernel/msi_sn.c index bed4183a5e..6ffd1f850d 100644 --- a/drivers/pci/msi-altix.c +++ b/arch/ia64/sn/kernel/msi_sn.c @@ -7,8 +7,10 @@ */ #include +#include #include #include +#include #include #include @@ -16,17 +18,16 @@ #include #include -#include "msi.h" - struct sn_msi_info { u64 pci_addr; struct sn_irq_info *sn_irq_info; }; -static struct sn_msi_info *sn_msi_info; +static struct sn_msi_info sn_msi_info[NR_IRQS]; + +static struct irq_chip sn_msi_chip; -static void -sn_msi_teardown(unsigned int vector) +void sn_teardown_msi_irq(unsigned int irq) { nasid_t nasid; int widget; @@ -36,7 +37,7 @@ sn_msi_teardown(unsigned int vector) struct pcibus_bussoft *bussoft; struct sn_pcibus_provider *provider; - sn_irq_info = sn_msi_info[vector].sn_irq_info; + sn_irq_info = sn_msi_info[irq].sn_irq_info; if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) return; @@ -45,9 +46,9 @@ sn_msi_teardown(unsigned int vector) provider = SN_PCIDEV_BUSPROVIDER(pdev); (*provider->dma_unmap)(pdev, - sn_msi_info[vector].pci_addr, + sn_msi_info[irq].pci_addr, PCI_DMA_FROMDEVICE); - sn_msi_info[vector].pci_addr = 0; + sn_msi_info[irq].pci_addr = 0; bussoft = SN_PCIDEV_BUSSOFT(pdev); nasid = NASID_GET(bussoft->bs_base); @@ -56,15 +57,15 @@ sn_msi_teardown(unsigned int vector) SWIN_WIDGETNUM(bussoft->bs_base); sn_intr_free(nasid, widget, sn_irq_info); - sn_msi_info[vector].sn_irq_info = NULL; + sn_msi_info[irq].sn_irq_info = NULL; return; } -int -sn_msi_setup(struct pci_dev *pdev, unsigned int vector, - u32 *addr_hi, u32 *addr_lo, u32 *data) +int sn_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) { + struct msi_msg msg; + struct msi_desc *entry; int widget; int status; nasid_t nasid; @@ -73,6 +74,10 @@ sn_msi_setup(struct pci_dev *pdev, unsigned int vector, struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev); struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); + entry = get_irq_data(irq); + if (!entry->msi_attrib.is_64) + return -EINVAL; + if (bussoft == NULL) return -EINVAL; @@ -93,7 +98,7 @@ sn_msi_setup(struct pci_dev *pdev, unsigned int vector, if (! sn_irq_info) return -ENOMEM; - status = sn_intr_alloc(nasid, widget, sn_irq_info, vector, -1, -1); + status = sn_intr_alloc(nasid, widget, sn_irq_info, irq, -1, -1); if (status) { kfree(sn_irq_info); return -ENOMEM; @@ -119,29 +124,32 @@ sn_msi_setup(struct pci_dev *pdev, unsigned int vector, return -ENOMEM; } - sn_msi_info[vector].sn_irq_info = sn_irq_info; - sn_msi_info[vector].pci_addr = bus_addr; + sn_msi_info[irq].sn_irq_info = sn_irq_info; + sn_msi_info[irq].pci_addr = bus_addr; - *addr_hi = (u32)(bus_addr >> 32); - *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); + msg.address_hi = (u32)(bus_addr >> 32); + msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); /* * In the SN platform, bit 16 is a "send vector" bit which * must be present in order to move the vector through the system. */ - *data = 0x100 + (unsigned int)vector; + msg.data = 0x100 + irq; #ifdef CONFIG_SMP - set_irq_affinity_info((vector & 0xff), sn_irq_info->irq_cpuid, 0); + set_irq_affinity_info(irq, sn_irq_info->irq_cpuid, 0); #endif + write_msi_msg(irq, &msg); + set_irq_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); + return 0; } -static void -sn_msi_target(unsigned int vector, unsigned int cpu, - u32 *addr_hi, u32 *addr_lo) +#ifdef CONFIG_SMP +static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) { + struct msi_msg msg; int slice; nasid_t nasid; u64 bus_addr; @@ -150,8 +158,10 @@ sn_msi_target(unsigned int vector, unsigned int cpu, struct sn_irq_info *sn_irq_info; struct sn_irq_info *new_irq_info; struct sn_pcibus_provider *provider; + unsigned int cpu; - sn_irq_info = sn_msi_info[vector].sn_irq_info; + cpu = first_cpu(cpu_mask); + sn_irq_info = sn_msi_info[irq].sn_irq_info; if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) return; @@ -159,19 +169,20 @@ sn_msi_target(unsigned int vector, unsigned int cpu, * Release XIO resources for the old MSI PCI address */ + read_msi_msg(irq, &msg); sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; pdev = sn_pdev->pdi_linux_pcidev; provider = SN_PCIDEV_BUSPROVIDER(pdev); - bus_addr = (u64)(*addr_hi) << 32 | (u64)(*addr_lo); + bus_addr = (u64)(msg.address_hi) << 32 | (u64)(msg.address_lo); (*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE); - sn_msi_info[vector].pci_addr = 0; + sn_msi_info[irq].pci_addr = 0; nasid = cpuid_to_nasid(cpu); slice = cpuid_to_slice(cpu); new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice); - sn_msi_info[vector].sn_irq_info = new_irq_info; + sn_msi_info[irq].sn_irq_info = new_irq_info; if (new_irq_info == NULL) return; @@ -184,27 +195,36 @@ sn_msi_target(unsigned int vector, unsigned int cpu, sizeof(new_irq_info->irq_xtalkaddr), SN_DMA_MSI|SN_DMA_ADDR_XIO); - sn_msi_info[vector].pci_addr = bus_addr; - *addr_hi = (u32)(bus_addr >> 32); - *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); + sn_msi_info[irq].pci_addr = bus_addr; + msg.address_hi = (u32)(bus_addr >> 32); + msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); + + write_msi_msg(irq, &msg); + set_native_irq_info(irq, cpu_mask); } +#endif /* CONFIG_SMP */ -struct msi_ops sn_msi_ops = { - .setup = sn_msi_setup, - .teardown = sn_msi_teardown, -#ifdef CONFIG_SMP - .target = sn_msi_target, -#endif -}; +static void sn_ack_msi_irq(unsigned int irq) +{ + move_native_irq(irq); + ia64_eoi(); +} -int -sn_msi_init(void) +static int sn_msi_retrigger_irq(unsigned int irq) { - sn_msi_info = - kzalloc(sizeof(struct sn_msi_info) * NR_VECTORS, GFP_KERNEL); - if (! sn_msi_info) - return -ENOMEM; + unsigned int vector = irq; + ia64_resend_irq(vector); - msi_register(&sn_msi_ops); - return 0; + return 1; } + +static struct irq_chip sn_msi_chip = { + .name = "PCI-MSI", + .mask = mask_msi_irq, + .unmask = unmask_msi_irq, + .ack = sn_ack_msi_irq, +#ifdef CONFIG_SMP + .set_affinity = sn_set_msi_irq_affinity, +#endif + .retrigger = sn_msi_retrigger_irq, +}; diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index c383d56bbe..003520b563 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -113,7 +113,7 @@ endif endif quiet_cmd_wrap = WRAP $@ - cmd_wrap =$(wrapper) -c -o $@ -p $2 $(CROSSWRAP) vmlinux + cmd_wrap =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) vmlinux quiet_cmd_wrap_initrd = WRAP $@ cmd_wrap_initrd =$(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \ -i $(obj)/ramdisk.image.gz vmlinux diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 723972bb5b..3ee03a9a98 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -341,7 +341,7 @@ static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); if (id == PCI_CAP_ID_HT) { id = readb(devbase + pos + 3); - if (id == 0x80) + if (id == HT_CAPTYPE_IRQ) break; } } diff --git a/arch/x86_64/kernel/i8259.c b/arch/x86_64/kernel/i8259.c index 2dd51f364e..0612a33bb8 100644 --- a/arch/x86_64/kernel/i8259.c +++ b/arch/x86_64/kernel/i8259.c @@ -43,17 +43,10 @@ BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ BI(x,c) BI(x,d) BI(x,e) BI(x,f) -#define BUILD_15_IRQS(x) \ - BI(x,0) BI(x,1) BI(x,2) BI(x,3) \ - BI(x,4) BI(x,5) BI(x,6) BI(x,7) \ - BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ - BI(x,c) BI(x,d) BI(x,e) - /* * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts: * (these are usually mapped to vectors 0x20-0x2f) */ -BUILD_16_IRQS(0x0) /* * The IO-APIC gives us many more interrupt sources. Most of these @@ -65,17 +58,12 @@ BUILD_16_IRQS(0x0) * * (these are usually mapped into the 0x30-0xff vector range) */ - BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) + BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7) BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb) -BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) - -#ifdef CONFIG_PCI_MSI - BUILD_15_IRQS(0xe) -#endif +BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf) #undef BUILD_16_IRQS -#undef BUILD_15_IRQS #undef BI @@ -88,29 +76,15 @@ BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f) -#define IRQLIST_15(x) \ - IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \ - IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \ - IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ - IRQ(x,c), IRQ(x,d), IRQ(x,e) - void (*interrupt[NR_IRQS])(void) = { - IRQLIST_16(0x0), - - IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3), + IRQLIST_16(0x2), IRQLIST_16(0x3), IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7), IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb), - IRQLIST_16(0xc), IRQLIST_16(0xd) - -#ifdef CONFIG_PCI_MSI - , IRQLIST_15(0xe) -#endif - + IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf) }; #undef IRQ #undef IRQLIST_16 -#undef IRQLIST_14 /* * This is the 'legacy' 8259A Programmable Interrupt Controller, @@ -121,42 +95,15 @@ void (*interrupt[NR_IRQS])(void) = { * moves to arch independent land */ -DEFINE_SPINLOCK(i8259A_lock); - static int i8259A_auto_eoi; - -static void end_8259A_irq (unsigned int irq) -{ - if (irq > 256) { - char var; - printk("return %p stack %p ti %p\n", __builtin_return_address(0), &var, task_thread_info(current)); - - BUG(); - } - - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && - irq_desc[irq].action) - enable_8259A_irq(irq); -} - -#define shutdown_8259A_irq disable_8259A_irq - +DEFINE_SPINLOCK(i8259A_lock); static void mask_and_ack_8259A(unsigned int); -static unsigned int startup_8259A_irq(unsigned int irq) -{ - enable_8259A_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type i8259A_irq_type = { - .typename = "XT-PIC", - .startup = startup_8259A_irq, - .shutdown = shutdown_8259A_irq, - .enable = enable_8259A_irq, - .disable = disable_8259A_irq, - .ack = mask_and_ack_8259A, - .end = end_8259A_irq, +static struct irq_chip i8259A_chip = { + .name = "XT-PIC", + .mask = disable_8259A_irq, + .unmask = enable_8259A_irq, + .mask_ack = mask_and_ack_8259A, }; /* @@ -231,7 +178,7 @@ void make_8259A_irq(unsigned int irq) { disable_irq_nosync(irq); io_apic_irqs &= ~(1<= NR_IRQS) - break; if (vector != IA32_SYSCALL_VECTOR) set_intr_gate(vector, interrupt[i]); } @@ -554,7 +520,7 @@ void __init init_IRQ(void) * IRQ0 must be given a fixed assignment and initialized, * because it's used before the IO-APIC is set up. */ - set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); + __get_cpu_var(vector_irq)[FIRST_DEVICE_VECTOR] = 0; /* * The reschedule interrupt is a CPU-to-CPU reschedule-helper diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 0491019d4c..91728d9d34 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c @@ -26,9 +26,12 @@ #include #include #include +#include #include #include #include +#include +#include #ifdef CONFIG_ACPI #include #endif @@ -41,6 +44,10 @@ #include #include #include +#include +#include + +static int assign_irq_vector(int irq, cpumask_t mask); #define __apicdebuginit __init @@ -81,14 +88,6 @@ static struct irq_pin_list { short apic, pin, next; } irq_2_pin[PIN_MAP_SIZE]; -int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1}; -#ifdef CONFIG_PCI_MSI -#define vector_to_irq(vector) \ - (platform_legacy_irq(vector) ? vector : vector_irq[vector]) -#else -#define vector_to_irq(vector) (vector) -#endif - #define __DO_ACTION(R, ACTION, FINAL) \ \ { \ @@ -139,11 +138,35 @@ static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) } #ifdef CONFIG_SMP +static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) +{ + int apic, pin; + struct irq_pin_list *entry = irq_2_pin + irq; + + BUG_ON(irq >= NR_IRQS); + for (;;) { + unsigned int reg; + apic = entry->apic; + pin = entry->pin; + if (pin == -1) + break; + io_apic_write(apic, 0x11 + pin*2, dest); + reg = io_apic_read(apic, 0x10 + pin*2); + reg &= ~0x000000ff; + reg |= vector; + io_apic_modify(apic, reg); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } +} + static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) { unsigned long flags; unsigned int dest; cpumask_t tmp; + int vector; cpus_and(tmp, mask, cpu_online_map); if (cpus_empty(tmp)) @@ -151,7 +174,13 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) cpus_and(mask, tmp, CPU_MASK_ALL); - dest = cpu_mask_to_apicid(mask); + vector = assign_irq_vector(irq, mask); + if (vector < 0) + return; + + cpus_clear(tmp); + cpu_set(vector >> 8, tmp); + dest = cpu_mask_to_apicid(tmp); /* * Only the high 8 bits are valid. @@ -159,14 +188,12 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) dest = SET_APIC_LOGICAL_ID(dest); spin_lock_irqsave(&ioapic_lock, flags); - __DO_ACTION(1, = dest, ) - set_irq_info(irq, mask); + __target_IO_APIC_irq(irq, dest, vector & 0xff); + set_native_irq_info(irq, mask); spin_unlock_irqrestore(&ioapic_lock, flags); } #endif -static u8 gsi_2_irq[NR_IRQ_VECTORS] = { [0 ... NR_IRQ_VECTORS-1] = 0xFF }; - /* * The common case is 1:1 IRQ<->pin mappings. Sometimes there are * shared ISA-space IRQs, so we have to support them. We are super @@ -492,64 +519,6 @@ static inline int irq_trigger(int idx) return MPBIOS_trigger(idx); } -static int next_irq = 16; - -/* - * gsi_irq_sharing -- Name overload! "irq" can be either a legacy IRQ - * in the range 0-15, a linux IRQ in the range 0-223, or a GSI number - * from ACPI, which can reach 800 in large boxen. - * - * Compact the sparse GSI space into a sequential IRQ series and reuse - * vectors if possible. - */ -int gsi_irq_sharing(int gsi) -{ - int i, tries, vector; - - BUG_ON(gsi >= NR_IRQ_VECTORS); - - if (platform_legacy_irq(gsi)) - return gsi; - - if (gsi_2_irq[gsi] != 0xFF) - return (int)gsi_2_irq[gsi]; - - tries = NR_IRQS; - try_again: - vector = assign_irq_vector(gsi); - - /* - * Sharing vectors means sharing IRQs, so scan irq_vectors for previous - * use of vector and if found, return that IRQ. However, we never want - * to share legacy IRQs, which usually have a different trigger mode - * than PCI. - */ - for (i = 0; i < NR_IRQS; i++) - if (IO_APIC_VECTOR(i) == vector) - break; - if (platform_legacy_irq(i)) { - if (--tries >= 0) { - IO_APIC_VECTOR(i) = 0; - goto try_again; - } - panic("gsi_irq_sharing: didn't find an IRQ using vector 0x%02X for GSI %d", vector, gsi); - } - if (i < NR_IRQS) { - gsi_2_irq[gsi] = i; - printk(KERN_INFO "GSI %d sharing vector 0x%02X and IRQ %d\n", - gsi, vector, i); - return i; - } - - i = next_irq++; - BUG_ON(i >= NR_IRQS); - gsi_2_irq[gsi] = i; - IO_APIC_VECTOR(i) = vector; - printk(KERN_INFO "GSI %d assigned vector 0x%02X and IRQ %d\n", - gsi, vector, i); - return i; -} - static int pin_2_irq(int idx, int apic, int pin) { int irq, i; @@ -571,7 +540,6 @@ static int pin_2_irq(int idx, int apic, int pin) while (i < apic) irq += nr_ioapic_registers[i++]; irq += pin; - irq = gsi_irq_sharing(irq); } BUG_ON(irq >= NR_IRQS); return irq; @@ -595,46 +563,83 @@ static inline int IO_APIC_irq_trigger(int irq) } /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ -u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 }; +unsigned int irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_EXTERNAL_VECTOR, 0 }; -int assign_irq_vector(int irq) +static int __assign_irq_vector(int irq, cpumask_t mask) { - static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; - unsigned long flags; - int vector; - - BUG_ON(irq != AUTO_ASSIGN && (unsigned)irq >= NR_IRQ_VECTORS); - - spin_lock_irqsave(&vector_lock, flags); - - if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0) { - spin_unlock_irqrestore(&vector_lock, flags); - return IO_APIC_VECTOR(irq); + /* + * NOTE! The local APIC isn't very good at handling + * multiple interrupts at the same interrupt level. + * As the interrupt level is determined by taking the + * vector number and shifting that right by 4, we + * want to spread these out a bit so that they don't + * all fall in the same interrupt level. + * + * Also, we've got to be careful not to trash gate + * 0x80, because int 0x80 is hm, kind of importantish. ;) + */ + static struct { + int vector; + int offset; + } pos[NR_CPUS] = { [ 0 ... NR_CPUS - 1] = {FIRST_DEVICE_VECTOR, 0} }; + int old_vector = -1; + int cpu; + + BUG_ON((unsigned)irq >= NR_IRQ_VECTORS); + + if (IO_APIC_VECTOR(irq) > 0) + old_vector = IO_APIC_VECTOR(irq); + if ((old_vector > 0) && cpu_isset(old_vector >> 8, mask)) { + return old_vector; } + + for_each_cpu_mask(cpu, mask) { + int vector, offset; + vector = pos[cpu].vector; + offset = pos[cpu].offset; next: - current_vector += 8; - if (current_vector == IA32_SYSCALL_VECTOR) - goto next; - - if (current_vector >= FIRST_SYSTEM_VECTOR) { - /* If we run out of vectors on large boxen, must share them. */ - offset = (offset + 1) % 8; - current_vector = FIRST_DEVICE_VECTOR + offset; + vector += 8; + if (vector >= FIRST_SYSTEM_VECTOR) { + /* If we run out of vectors on large boxen, must share them. */ + offset = (offset + 1) % 8; + vector = FIRST_DEVICE_VECTOR + offset; + } + if (unlikely(pos[cpu].vector == vector)) + continue; + if (vector == IA32_SYSCALL_VECTOR) + goto next; + if (per_cpu(vector_irq, cpu)[vector] != -1) + goto next; + /* Found one! */ + pos[cpu].vector = vector; + pos[cpu].offset = offset; + if (old_vector >= 0) { + int old_cpu = old_vector >> 8; + old_vector &= 0xff; + per_cpu(vector_irq, old_cpu)[old_vector] = -1; + } + per_cpu(vector_irq, cpu)[vector] = irq; + vector |= cpu << 8; + IO_APIC_VECTOR(irq) = vector; + return vector; } + return -ENOSPC; +} - vector = current_vector; - vector_irq[vector] = irq; - if (irq != AUTO_ASSIGN) - IO_APIC_VECTOR(irq) = vector; +static int assign_irq_vector(int irq, cpumask_t mask) +{ + int vector; + unsigned long flags; + spin_lock_irqsave(&vector_lock, flags); + vector = __assign_irq_vector(irq, mask); spin_unlock_irqrestore(&vector_lock, flags); - return vector; } extern void (*interrupt[NR_IRQS])(void); -static struct hw_interrupt_type ioapic_level_type; -static struct hw_interrupt_type ioapic_edge_type; + +static struct irq_chip ioapic_chip; #define IOAPIC_AUTO -1 #define IOAPIC_EDGE 0 @@ -642,16 +647,13 @@ static struct hw_interrupt_type ioapic_edge_type; static void ioapic_register_intr(int irq, int vector, unsigned long trigger) { - unsigned idx; - - idx = use_pci_vector() && !platform_legacy_irq(irq) ? vector : irq; - if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || trigger == IOAPIC_LEVEL) - irq_desc[idx].chip = &ioapic_level_type; + set_irq_chip_and_handler(irq, &ioapic_chip, + handle_fasteoi_irq); else - irq_desc[idx].chip = &ioapic_edge_type; - set_intr_gate(vector, interrupt[idx]); + set_irq_chip_and_handler(irq, &ioapic_chip, + handle_edge_irq); } static void __init setup_IO_APIC_irqs(void) @@ -701,8 +703,15 @@ static void __init setup_IO_APIC_irqs(void) continue; if (IO_APIC_IRQ(irq)) { - vector = assign_irq_vector(irq); - entry.vector = vector; + cpumask_t mask; + vector = assign_irq_vector(irq, TARGET_CPUS); + if (vector < 0) + continue; + + cpus_clear(mask); + cpu_set(vector >> 8, mask); + entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); + entry.vector = vector & 0xff; ioapic_register_intr(irq, vector, IOAPIC_AUTO); if (!apic && (irq < 16)) @@ -752,7 +761,7 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in * The timer IRQ doesn't have to know that behind the * scene we have a 8259A-master in AEOI mode ... */ - irq_desc[0].chip = &ioapic_edge_type; + set_irq_chip_and_handler(0, &ioapic_chip, handle_edge_irq); /* * Add it to the IO-APIC irq-routing table: @@ -868,17 +877,12 @@ void __apicdebuginit print_IO_APIC(void) ); } } - if (use_pci_vector()) - printk(KERN_INFO "Using vector-based indexing\n"); printk(KERN_DEBUG "IRQ to pin mappings:\n"); for (i = 0; i < NR_IRQS; i++) { struct irq_pin_list *entry = irq_2_pin + i; if (entry->pin < 0) continue; - if (use_pci_vector() && !platform_legacy_irq(i)) - printk(KERN_DEBUG "IRQ%d ", IO_APIC_VECTOR(i)); - else - printk(KERN_DEBUG "IRQ%d ", i); + printk(KERN_DEBUG "IRQ%d ", i); for (;;) { printk("-> %d:%d", entry->apic, entry->pin); if (!entry->next) @@ -1185,7 +1189,7 @@ static int __init timer_irq_works(void) * an edge even if it isn't on the 8259A... */ -static unsigned int startup_edge_ioapic_irq(unsigned int irq) +static unsigned int startup_ioapic_irq(unsigned int irq) { int was_pending = 0; unsigned long flags; @@ -1202,107 +1206,16 @@ static unsigned int startup_edge_ioapic_irq(unsigned int irq) return was_pending; } -/* - * Once we have recorded IRQ_PENDING already, we can mask the - * interrupt for real. This prevents IRQ storms from unhandled - * devices. - */ -static void ack_edge_ioapic_irq(unsigned int irq) -{ - move_irq(irq); - if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) - == (IRQ_PENDING | IRQ_DISABLED)) - mask_IO_APIC_irq(irq); - ack_APIC_irq(); -} - -/* - * Level triggered interrupts can just be masked, - * and shutting down and starting up the interrupt - * is the same as enabling and disabling them -- except - * with a startup need to return a "was pending" value. - * - * Level triggered interrupts are special because we - * do not touch any IO-APIC register while handling - * them. We ack the APIC in the end-IRQ handler, not - * in the start-IRQ-handler. Protection against reentrance - * from the same interrupt is still provided, both by the - * generic IRQ layer and by the fact that an unacked local - * APIC does not accept IRQs. - */ -static unsigned int startup_level_ioapic_irq (unsigned int irq) -{ - unmask_IO_APIC_irq(irq); - - return 0; /* don't check for pending */ -} - -static void end_level_ioapic_irq (unsigned int irq) -{ - move_irq(irq); - ack_APIC_irq(); -} - -#ifdef CONFIG_PCI_MSI -static unsigned int startup_edge_ioapic_vector(unsigned int vector) -{ - int irq = vector_to_irq(vector); - - return startup_edge_ioapic_irq(irq); -} - -static void ack_edge_ioapic_vector(unsigned int vector) -{ - int irq = vector_to_irq(vector); - - move_native_irq(vector); - ack_edge_ioapic_irq(irq); -} - -static unsigned int startup_level_ioapic_vector (unsigned int vector) +static int ioapic_retrigger_irq(unsigned int irq) { - int irq = vector_to_irq(vector); + cpumask_t mask; + unsigned vector; - return startup_level_ioapic_irq (irq); -} - -static void end_level_ioapic_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - move_native_irq(vector); - end_level_ioapic_irq(irq); -} - -static void mask_IO_APIC_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - mask_IO_APIC_irq(irq); -} + vector = irq_vector[irq]; + cpus_clear(mask); + cpu_set(vector >> 8, mask); -static void unmask_IO_APIC_vector (unsigned int vector) -{ - int irq = vector_to_irq(vector); - - unmask_IO_APIC_irq(irq); -} - -#ifdef CONFIG_SMP -static void set_ioapic_affinity_vector (unsigned int vector, - cpumask_t cpu_mask) -{ - int irq = vector_to_irq(vector); - - set_native_irq_info(vector, cpu_mask); - set_ioapic_affinity_irq(irq, cpu_mask); -} -#endif // CONFIG_SMP -#endif // CONFIG_PCI_MSI - -static int ioapic_retrigger(unsigned int irq) -{ - send_IPI_self(IO_APIC_VECTOR(irq)); + send_IPI_mask(mask, vector & 0xff); return 1; } @@ -1316,32 +1229,47 @@ static int ioapic_retrigger(unsigned int irq) * races. */ -static struct hw_interrupt_type ioapic_edge_type __read_mostly = { - .typename = "IO-APIC-edge", - .startup = startup_edge_ioapic, - .shutdown = shutdown_edge_ioapic, - .enable = enable_edge_ioapic, - .disable = disable_edge_ioapic, - .ack = ack_edge_ioapic, - .end = end_edge_ioapic, -#ifdef CONFIG_SMP - .set_affinity = set_ioapic_affinity, +static void ack_apic_edge(unsigned int irq) +{ + move_native_irq(irq); + ack_APIC_irq(); +} + +static void ack_apic_level(unsigned int irq) +{ + int do_unmask_irq = 0; + +#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) + /* If we are moving the irq we need to mask it */ + if (unlikely(irq_desc[irq].status & IRQ_MOVE_PENDING)) { + do_unmask_irq = 1; + mask_IO_APIC_irq(irq); + } #endif - .retrigger = ioapic_retrigger, -}; -static struct hw_interrupt_type ioapic_level_type __read_mostly = { - .typename = "IO-APIC-level", - .startup = startup_level_ioapic, - .shutdown = shutdown_level_ioapic, - .enable = enable_level_ioapic, - .disable = disable_level_ioapic, - .ack = mask_and_ack_level_ioapic, - .end = end_level_ioapic, + /* + * We must acknowledge the irq before we move it or the acknowledge will + * not propogate properly. + */ + ack_APIC_irq(); + + /* Now we can move and renable the irq */ + move_masked_irq(irq); + if (unlikely(do_unmask_irq)) + unmask_IO_APIC_irq(irq); +} + +static struct irq_chip ioapic_chip __read_mostly = { + .name = "IO-APIC", + .startup = startup_ioapic_irq, + .mask = mask_IO_APIC_irq, + .unmask = unmask_IO_APIC_irq, + .ack = ack_apic_edge, + .eoi = ack_apic_level, #ifdef CONFIG_SMP - .set_affinity = set_ioapic_affinity, + .set_affinity = set_ioapic_affinity_irq, #endif - .retrigger = ioapic_retrigger, + .retrigger = ioapic_retrigger_irq, }; static inline void init_IO_APIC_traps(void) @@ -1361,11 +1289,6 @@ static inline void init_IO_APIC_traps(void) */ for (irq = 0; irq < NR_IRQS ; irq++) { int tmp = irq; - if (use_pci_vector()) { - if (!platform_legacy_irq(tmp)) - if ((tmp = vector_to_irq(tmp)) == -1) - continue; - } if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) { /* * Hmm.. We don't have an entry for this, @@ -1376,7 +1299,7 @@ static inline void init_IO_APIC_traps(void) make_8259A_irq(irq); else /* Strange. Oh, well.. */ - irq_desc[irq].chip = &no_irq_type; + irq_desc[irq].chip = &no_irq_chip; } } } @@ -1495,8 +1418,6 @@ static inline void unlock_ExtINT_logic(void) spin_unlock_irqrestore(&ioapic_lock, flags); } -int timer_uses_ioapic_pin_0; - /* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ @@ -1514,8 +1435,7 @@ static inline void check_timer(void) * get/set the timer IRQ vector: */ disable_8259A_irq(0); - vector = assign_irq_vector(0); - set_intr_gate(vector, interrupt[0]); + vector = assign_irq_vector(0, TARGET_CPUS); /* * Subtle, code in do_timer_interrupt() expects an AEOI @@ -1534,9 +1454,6 @@ static inline void check_timer(void) pin2 = ioapic_i8259.pin; apic2 = ioapic_i8259.apic; - if (pin1 == 0) - timer_uses_ioapic_pin_0 = 1; - apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", vector, apic1, pin1, apic2, pin2); @@ -1740,6 +1657,253 @@ static int __init ioapic_init_sysfs(void) device_initcall(ioapic_init_sysfs); +/* + * Dynamic irq allocate and deallocation + */ +int create_irq(void) +{ + /* Allocate an unused irq */ + int irq; + int new; + int vector = 0; + unsigned long flags; + + irq = -ENOSPC; + spin_lock_irqsave(&vector_lock, flags); + for (new = (NR_IRQS - 1); new >= 0; new--) { + if (platform_legacy_irq(new)) + continue; + if (irq_vector[new] != 0) + continue; + vector = __assign_irq_vector(new, TARGET_CPUS); + if (likely(vector > 0)) + irq = new; + break; + } + spin_unlock_irqrestore(&vector_lock, flags); + + if (irq >= 0) { + dynamic_irq_init(irq); + } + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + spin_lock_irqsave(&vector_lock, flags); + irq_vector[irq] = 0; + spin_unlock_irqrestore(&vector_lock, flags); +} + +/* + * MSI mesage composition + */ +#ifdef CONFIG_PCI_MSI +static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg) +{ + int vector; + unsigned dest; + + vector = assign_irq_vector(irq, TARGET_CPUS); + if (vector >= 0) { + cpumask_t tmp; + + cpus_clear(tmp); + cpu_set(vector >> 8, tmp); + dest = cpu_mask_to_apicid(tmp); + + msg->address_hi = MSI_ADDR_BASE_HI; + msg->address_lo = + MSI_ADDR_BASE_LO | + ((INT_DEST_MODE == 0) ? + MSI_ADDR_DEST_MODE_PHYSICAL: + MSI_ADDR_DEST_MODE_LOGICAL) | + ((INT_DELIVERY_MODE != dest_LowestPrio) ? + MSI_ADDR_REDIRECTION_CPU: + MSI_ADDR_REDIRECTION_LOWPRI) | + MSI_ADDR_DEST_ID(dest); + + msg->data = + MSI_DATA_TRIGGER_EDGE | + MSI_DATA_LEVEL_ASSERT | + ((INT_DELIVERY_MODE != dest_LowestPrio) ? + MSI_DATA_DELIVERY_FIXED: + MSI_DATA_DELIVERY_LOWPRI) | + MSI_DATA_VECTOR(vector); + } + return vector; +} + +#ifdef CONFIG_SMP +static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) +{ + struct msi_msg msg; + unsigned int dest; + cpumask_t tmp; + int vector; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + tmp = TARGET_CPUS; + + cpus_and(mask, tmp, CPU_MASK_ALL); + + vector = assign_irq_vector(irq, mask); + if (vector < 0) + return; + + cpus_clear(tmp); + cpu_set(vector >> 8, tmp); + dest = cpu_mask_to_apicid(tmp); + + read_msi_msg(irq, &msg); + + msg.data &= ~MSI_DATA_VECTOR_MASK; + msg.data |= MSI_DATA_VECTOR(vector); + msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; + msg.address_lo |= MSI_ADDR_DEST_ID(dest); + + write_msi_msg(irq, &msg); + set_native_irq_info(irq, mask); +} +#endif /* CONFIG_SMP */ + +/* + * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, + * which implement the MSI or MSI-X Capability Structure. + */ +static struct irq_chip msi_chip = { + .name = "PCI-MSI", + .unmask = unmask_msi_irq, + .mask = mask_msi_irq, + .ack = ack_apic_edge, +#ifdef CONFIG_SMP + .set_affinity = set_msi_irq_affinity, +#endif + .retrigger = ioapic_retrigger_irq, +}; + +int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev) +{ + struct msi_msg msg; + int ret; + ret = msi_compose_msg(dev, irq, &msg); + if (ret < 0) + return ret; + + write_msi_msg(irq, &msg); + + set_irq_chip_and_handler(irq, &msi_chip, handle_edge_irq); + + return 0; +} + +void arch_teardown_msi_irq(unsigned int irq) +{ + return; +} + +#endif /* CONFIG_PCI_MSI */ + +/* + * Hypertransport interrupt support + */ +#ifdef CONFIG_HT_IRQ + +#ifdef CONFIG_SMP + +static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector) +{ + u32 low, high; + low = read_ht_irq_low(irq); + high = read_ht_irq_high(irq); + + low &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK); + high &= ~(HT_IRQ_HIGH_DEST_ID_MASK); + + low |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest); + high |= HT_IRQ_HIGH_DEST_ID(dest); + + write_ht_irq_low(irq, low); + write_ht_irq_high(irq, high); +} + +static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) +{ + unsigned int dest; + cpumask_t tmp; + int vector; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + tmp = TARGET_CPUS; + + cpus_and(mask, tmp, CPU_MASK_ALL); + + vector = assign_irq_vector(irq, mask); + if (vector < 0) + return; + + cpus_clear(tmp); + cpu_set(vector >> 8, tmp); + dest = cpu_mask_to_apicid(tmp); + + target_ht_irq(irq, dest, vector & 0xff); + set_native_irq_info(irq, mask); +} +#endif + +static struct hw_interrupt_type ht_irq_chip = { + .name = "PCI-HT", + .mask = mask_ht_irq, + .unmask = unmask_ht_irq, + .ack = ack_apic_edge, +#ifdef CONFIG_SMP + .set_affinity = set_ht_irq_affinity, +#endif + .retrigger = ioapic_retrigger_irq, +}; + +int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) +{ + int vector; + + vector = assign_irq_vector(irq, TARGET_CPUS); + if (vector >= 0) { + u32 low, high; + unsigned dest; + cpumask_t tmp; + + cpus_clear(tmp); + cpu_set(vector >> 8, tmp); + dest = cpu_mask_to_apicid(tmp); + + high = HT_IRQ_HIGH_DEST_ID(dest); + + low = HT_IRQ_LOW_BASE | + HT_IRQ_LOW_DEST_ID(dest) | + HT_IRQ_LOW_VECTOR(vector) | + ((INT_DEST_MODE == 0) ? + HT_IRQ_LOW_DM_PHYSICAL : + HT_IRQ_LOW_DM_LOGICAL) | + HT_IRQ_LOW_RQEOI_EDGE | + ((INT_DELIVERY_MODE != dest_LowestPrio) ? + HT_IRQ_LOW_MT_FIXED : + HT_IRQ_LOW_MT_ARBITRATED); + + write_ht_irq_low(irq, low); + write_ht_irq_high(irq, high); + + set_irq_chip_and_handler(irq, &ht_irq_chip, handle_edge_irq); + } + return vector; +} +#endif /* CONFIG_HT_IRQ */ + /* -------------------------------------------------------------------------- ACPI-based IOAPIC Configuration -------------------------------------------------------------------------- */ @@ -1765,6 +1929,8 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p { struct IO_APIC_route_entry entry; unsigned long flags; + int vector; + cpumask_t mask; if (!IO_APIC_IRQ(irq)) { apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n", @@ -1772,6 +1938,20 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p return -EINVAL; } + /* + * IRQs < 16 are already in the irq_2_pin[] map + */ + if (irq >= 16) + add_pin_to_irq(irq, ioapic, pin); + + + vector = assign_irq_vector(irq, TARGET_CPUS); + if (vector < 0) + return vector; + + cpus_clear(mask); + cpu_set(vector >> 8, mask); + /* * Generate a PCI IRQ routing entry and program the IOAPIC accordingly. * Note that we mask (disable) IRQs now -- these get enabled when the @@ -1782,19 +1962,11 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p entry.delivery_mode = INT_DELIVERY_MODE; entry.dest_mode = INT_DEST_MODE; - entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); + entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); entry.trigger = triggering; entry.polarity = polarity; entry.mask = 1; /* Disabled (masked) */ - - irq = gsi_irq_sharing(irq); - /* - * IRQs < 16 are already in the irq_2_pin[] map - */ - if (irq >= 16) - add_pin_to_irq(irq, ioapic, pin); - - entry.vector = assign_irq_vector(irq); + entry.vector = vector & 0xff; apic_printk(APIC_VERBOSE,KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " "IRQ %d Mode:%i Active:%i)\n", ioapic, @@ -1809,7 +1981,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p ioapic_write_entry(ioapic, pin, entry); spin_lock_irqsave(&ioapic_lock, flags); - set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); + set_native_irq_info(irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); return 0; diff --git a/arch/x86_64/kernel/irq.c b/arch/x86_64/kernel/irq.c index b3677e6ccc..506f27c85c 100644 --- a/arch/x86_64/kernel/irq.c +++ b/arch/x86_64/kernel/irq.c @@ -74,7 +74,8 @@ int show_interrupts(struct seq_file *p, void *v) for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif - seq_printf(p, " %14s", irq_desc[i].chip->typename); + seq_printf(p, " %8s", irq_desc[i].chip->name); + seq_printf(p, "-%s", handle_irq_name(irq_desc[i].handle_irq)); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) @@ -104,7 +105,12 @@ skip: asmlinkage unsigned int do_IRQ(struct pt_regs *regs) { /* high bit used in ret_from_ code */ - unsigned irq = ~regs->orig_rax; + unsigned vector = ~regs->orig_rax; + unsigned irq; + + exit_idle(); + irq_enter(); + irq = __get_cpu_var(vector_irq)[vector]; if (unlikely(irq >= NR_IRQS)) { printk(KERN_EMERG "%s: cannot handle IRQ %d\n", @@ -112,12 +118,10 @@ asmlinkage unsigned int do_IRQ(struct pt_regs *regs) BUG(); } - exit_idle(); - irq_enter(); #ifdef CONFIG_DEBUG_STACKOVERFLOW stack_overflow_check(regs); #endif - __do_IRQ(irq, regs); + generic_handle_irq(irq, regs); irq_exit(); return 1; diff --git a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c index b8d53dfa99..b147ab19fb 100644 --- a/arch/x86_64/kernel/mpparse.c +++ b/arch/x86_64/kernel/mpparse.c @@ -790,20 +790,11 @@ void __init mp_config_acpi_legacy_irqs(void) } } -#define MAX_GSI_NUM 4096 - int mp_register_gsi(u32 gsi, int triggering, int polarity) { int ioapic = -1; int ioapic_pin = 0; int idx, bit = 0; - static int pci_irq = 16; - /* - * Mapping between Global System Interrupts, which - * represent all possible interrupts, to the IRQs - * assigned to actual devices. - */ - static int gsi_to_irq[MAX_GSI_NUM]; if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC) return gsi; @@ -836,42 +827,11 @@ int mp_register_gsi(u32 gsi, int triggering, int polarity) if ((1< 15), but - * avoid a problem where the 8254 timer (IRQ0) is setup - * via an override (so it's not on pin 0 of the ioapic), - * and at the same time, the pin 0 interrupt is a PCI - * type. The gsi > 15 test could cause these two pins - * to be shared as IRQ0, and they are not shareable. - * So test for this condition, and if necessary, avoid - * the pin collision. - */ - if (gsi > 15 || (gsi == 0 && !timer_uses_ioapic_pin_0)) - gsi = pci_irq++; - /* - * Don't assign IRQ used by ACPI SCI - */ - if (gsi == acpi_fadt.sci_int) - gsi = pci_irq++; - gsi_to_irq[irq] = gsi; - } else { - printk(KERN_ERR "GSI %u is too high\n", gsi); - return gsi; - } - } - io_apic_set_pci_routing(ioapic, ioapic_pin, gsi, triggering == ACPI_EDGE_SENSITIVE ? 0 : 1, polarity == ACPI_ACTIVE_HIGH ? 0 : 1); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 1aabc81d82..54e1f38ce3 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -299,76 +299,46 @@ static const struct ata_port_info ahci_port_info[] = { static const struct pci_device_id ahci_pci_tbl[] = { /* Intel */ - { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH6 */ - { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH6M */ - { PCI_VENDOR_ID_INTEL, 0x27c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7 */ - { PCI_VENDOR_ID_INTEL, 0x27c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7M */ - { PCI_VENDOR_ID_INTEL, 0x27c3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7R */ - { PCI_VENDOR_ID_AL, 0x5288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ULi M5288 */ - { PCI_VENDOR_ID_INTEL, 0x2681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ESB2 */ - { PCI_VENDOR_ID_INTEL, 0x2682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ESB2 */ - { PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ESB2 */ - { PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH7-M DH */ - { PCI_VENDOR_ID_INTEL, 0x2821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8 */ - { PCI_VENDOR_ID_INTEL, 0x2822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8 */ - { PCI_VENDOR_ID_INTEL, 0x2824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8 */ - { PCI_VENDOR_ID_INTEL, 0x2829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8M */ - { PCI_VENDOR_ID_INTEL, 0x282a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ICH8M */ + { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */ + { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */ + { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */ + { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */ + { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */ + { PCI_VDEVICE(AL, 0x5288), board_ahci }, /* ULi M5288 */ + { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */ + { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */ + { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */ + { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */ + { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */ + { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */ + { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */ + { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */ + { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */ /* JMicron */ - { 0x197b, 0x2360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB360 */ - { 0x197b, 0x2361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB361 */ - { 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB363 */ - { 0x197b, 0x2365, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB365 */ - { 0x197b, 0x2366, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* JMicron JMB366 */ + { PCI_VDEVICE(JMICRON, 0x2360), board_ahci }, /* JMicron JMB360 */ + { PCI_VDEVICE(JMICRON, 0x2361), board_ahci }, /* JMicron JMB361 */ + { PCI_VDEVICE(JMICRON, 0x2363), board_ahci }, /* JMicron JMB363 */ + { PCI_VDEVICE(JMICRON, 0x2365), board_ahci }, /* JMicron JMB365 */ + { PCI_VDEVICE(JMICRON, 0x2366), board_ahci }, /* JMicron JMB366 */ /* ATI */ - { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ATI SB600 non-raid */ - { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* ATI SB600 raid */ + { PCI_VDEVICE(ATI, 0x4380), board_ahci }, /* ATI SB600 non-raid */ + { PCI_VDEVICE(ATI, 0x4381), board_ahci }, /* ATI SB600 raid */ /* VIA */ - { PCI_VENDOR_ID_VIA, 0x3349, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci_vt8251 }, /* VIA VT8251 */ + { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ /* NVIDIA */ - { PCI_VENDOR_ID_NVIDIA, 0x044c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - { PCI_VENDOR_ID_NVIDIA, 0x044d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - { PCI_VENDOR_ID_NVIDIA, 0x044e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ - { PCI_VENDOR_ID_NVIDIA, 0x044f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci }, /* MCP65 */ + { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci }, /* MCP65 */ /* SiS */ - { PCI_VENDOR_ID_SI, 0x1184, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* SiS 966 */ - { PCI_VENDOR_ID_SI, 0x1185, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* SiS 966 */ - { PCI_VENDOR_ID_SI, 0x0186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_ahci }, /* SiS 968 */ + { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */ + { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */ + { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ { } /* terminate list */ }; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index b4abd68503..dce65651d8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2340,7 +2340,8 @@ unsigned int ata_busy_sleep (struct ata_port *ap, if (status & ATA_BUSY) ata_port_printk(ap, KERN_WARNING, - "port is slow to respond, please be patient\n"); + "port is slow to respond, please be patient " + "(Status 0x%x)\n", status); timeout = timer_start + tmout; while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) { @@ -2350,7 +2351,8 @@ unsigned int ata_busy_sleep (struct ata_port *ap, if (status & ATA_BUSY) { ata_port_printk(ap, KERN_ERR, "port failed to respond " - "(%lu secs)\n", tmout / HZ); + "(%lu secs, Status 0x%x)\n", + tmout / HZ, status); return 1; } @@ -5478,11 +5480,10 @@ int ata_device_add(const struct ata_probe_ent *ent) int irq_line = ent->irq; ap = ata_port_add(ent, host, i); + host->ports[i] = ap; if (!ap) goto err_out; - host->ports[i] = ap; - /* dummy? */ if (ent->dummy_port_mask & (1 << i)) { ata_port_printk(ap, KERN_INFO, "DUMMY\n"); @@ -5740,7 +5741,7 @@ void ata_host_remove(struct ata_host *host) /** * ata_scsi_release - SCSI layer callback hook for host unload - * @host: libata host to be unloaded + * @shost: libata host to be unloaded * * Performs all duties necessary to shut down a libata port... * Kill port kthread, disable port, and release resources. @@ -5786,6 +5787,7 @@ ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) probe_ent->mwdma_mask = port->mwdma_mask; probe_ent->udma_mask = port->udma_mask; probe_ent->port_ops = port->port_ops; + probe_ent->private_data = port->private_data; return probe_ent; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 3986ec8741..b0d0cc41f3 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -889,6 +889,7 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) { struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev; + unsigned long flags; int max_depth; if (queue_depth < 1) @@ -904,6 +905,14 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) queue_depth = max_depth; scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth); + + spin_lock_irqsave(ap->lock, flags); + if (queue_depth > 1) + dev->flags &= ~ATA_DFLAG_NCQ_OFF; + else + dev->flags |= ATA_DFLAG_NCQ_OFF; + spin_unlock_irqrestore(ap->lock, flags); + return queue_depth; } @@ -1293,7 +1302,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm */ goto nothing_to_do; - if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) { + if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF | + ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) { /* yay, NCQ */ if (!lba_48_ok(block, n_block)) goto out_of_range; @@ -3174,7 +3184,7 @@ void ata_scsi_dev_rescan(void *data) /** * ata_sas_port_alloc - Allocate port for a SAS attached SATA device - * @pdev: PCI device that the scsi device is attached to + * @host: ATA host container for all SAS ports * @port_info: Information from low-level host driver * @shost: SCSI host that the scsi device is attached to * diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 08b3a40747..06daaa3736 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -828,7 +828,6 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int probe_ent->irq = pdev->irq; probe_ent->irq_flags = IRQF_SHARED; - probe_ent->private_data = port[0]->private_data; if (ports & ATA_PORT_PRIMARY) { probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 0); @@ -878,7 +877,6 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, return NULL; probe_ent->n_ports = 2; - probe_ent->private_data = port[0]->private_data; if (port_mask & ATA_PORT_PRIMARY) { probe_ent->irq = ATA_PRIMARY_IRQ; @@ -908,6 +906,8 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, probe_ent->_host_flags |= ATA_HOST_SIMPLEX; } ata_std_ports(&probe_ent->port[1]); + + /* FIXME: could be pointing to stack area; must copy */ probe_ent->pinfo2 = port[1]; } else probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY; @@ -946,35 +946,21 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, { struct ata_probe_ent *probe_ent = NULL; struct ata_port_info *port[2]; - u8 tmp8, mask; + u8 mask; unsigned int legacy_mode = 0; int disable_dev_on_err = 1; int rc; DPRINTK("ENTER\n"); + BUG_ON(n_ports < 1 || n_ports > 2); + port[0] = port_info[0]; if (n_ports > 1) port[1] = port_info[1]; else port[1] = port[0]; - if ((port[0]->flags & ATA_FLAG_NO_LEGACY) == 0 - && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { - /* TODO: What if one channel is in native mode ... */ - pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); - mask = (1 << 2) | (1 << 0); - if ((tmp8 & mask) != mask) - legacy_mode = (1 << 3); - } - - /* FIXME... */ - if ((!legacy_mode) && (n_ports > 2)) { - printk(KERN_ERR "ata: BUG: native mode, n_ports > 2\n"); - n_ports = 2; - /* For now */ - } - /* FIXME: Really for ATA it isn't safe because the device may be multi-purpose and we want to leave it alone if it was already enabled. Secondly for shared use as Arjan says we want refcounting @@ -987,6 +973,16 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, if (rc) return rc; + if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { + u8 tmp8; + + /* TODO: What if one channel is in native mode ... */ + pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); + mask = (1 << 2) | (1 << 0); + if ((tmp8 & mask) != mask) + legacy_mode = (1 << 3); + } + rc = pci_request_regions(pdev, DRV_NAME); if (rc) { disable_dev_on_err = 0; @@ -1039,7 +1035,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, goto err_out_regions; } - /* FIXME: If we get no DMA mask we should fall back to PIO */ + /* TODO: If we get no DMA mask we should fall back to PIO */ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) goto err_out_regions; @@ -1062,13 +1058,17 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, pci_set_master(pdev); - /* FIXME: check ata_device_add return */ - ata_device_add(probe_ent); + if (!ata_device_add(probe_ent)) { + rc = -ENODEV; + goto err_out_ent; + } kfree(probe_ent); return 0; +err_out_ent: + kfree(probe_ent); err_out_regions: if (legacy_mode & ATA_PORT_PRIMARY) release_region(ATA_PRIMARY_CMD, 8); diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index 87af3b5861..1d695df586 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -34,7 +34,7 @@ #include #define DRV_NAME "pata_ali" -#define DRV_VERSION "0.6.5" +#define DRV_VERSION "0.6.6" /* * Cable special cases @@ -630,7 +630,7 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_read_config_byte(pdev, 0x53, &tmp); if (rev <= 0x20) tmp &= ~0x02; - if (rev == 0xc7) + if (rev >= 0xc7) tmp |= 0x03; else tmp |= 0x01; /* CD_ROM enable for DMA */ @@ -644,10 +644,11 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } -static struct pci_device_id ali[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228), }, - { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229), }, - { 0, }, +static const struct pci_device_id ali[] = { + { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5228), }, + { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5229), }, + + { }, }; static struct pci_driver ali_pci_driver = { diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index 599ee26672..29234c8971 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -662,27 +662,28 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct pci_device_id amd[] = { - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, - { 0, }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_COBRA_7401), 0 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7409), 1 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7411), 3 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 4 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 7 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 8 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 9 }, + + { }, }; static struct pci_driver amd_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = amd, .probe = amd_init_one, .remove = ata_pci_remove_one @@ -698,7 +699,6 @@ static void __exit amd_exit(void) pci_unregister_driver(&amd_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for AMD PATA IDE"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c index c4ccb75a4f..690828eb52 100644 --- a/drivers/ata/pata_artop.c +++ b/drivers/ata/pata_artop.c @@ -426,7 +426,7 @@ static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id) .port_ops = &artop6260_ops, }; struct ata_port_info *port_info[2]; - struct ata_port_info *info; + struct ata_port_info *info = NULL; int ports = 2; if (!printed_version++) @@ -470,16 +470,20 @@ static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id) pci_write_config_byte(pdev, 0x4a, (reg & ~0x01) | 0x80); } + + BUG_ON(info == NULL); + port_info[0] = port_info[1] = info; return ata_pci_init_one(pdev, port_info, ports); } static const struct pci_device_id artop_pci_tbl[] = { - { 0x1191, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { 0x1191, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { 0x1191, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { 0x1191, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, - { 0x1191, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VDEVICE(ARTOP, 0x0005), 0 }, + { PCI_VDEVICE(ARTOP, 0x0006), 1 }, + { PCI_VDEVICE(ARTOP, 0x0007), 1 }, + { PCI_VDEVICE(ARTOP, 0x0008), 2 }, + { PCI_VDEVICE(ARTOP, 0x0009), 2 }, + { } /* terminate list */ }; @@ -500,7 +504,6 @@ static void __exit artop_exit(void) pci_unregister_driver(&artop_pci_driver); } - module_init(artop_init); module_exit(artop_exit); diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index 6c2269b6bd..1ce28d2125 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -267,12 +267,13 @@ static int atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static struct pci_device_id atiixp[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_IDE), }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE), }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), }, - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), }, - { 0, }, +static const struct pci_device_id atiixp[] = { + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP200_IDE), }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP300_IDE), }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), }, + + { }, }; static struct pci_driver atiixp_pci_driver = { @@ -293,7 +294,6 @@ static void __exit atiixp_exit(void) pci_unregister_driver(&atiixp_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for ATI IXP200/300/400"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index e92b0ef43e..b9bbd1d454 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -468,16 +468,17 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } -static struct pci_device_id cmd64x[] = { - { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, - { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, - { 0, }, +static const struct pci_device_id cmd64x[] = { + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 4 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 5 }, + + { }, }; static struct pci_driver cmd64x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = cmd64x, .probe = cmd64x_init_one, .remove = ata_pci_remove_one @@ -488,13 +489,11 @@ static int __init cmd64x_init(void) return pci_register_driver(&cmd64x_pci_driver); } - static void __exit cmd64x_exit(void) { pci_unregister_driver(&cmd64x_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for CMD64x series PATA controllers"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index a6c6cebd0d..2cd3c0ff76 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -299,10 +299,11 @@ static void __devexit cs5520_remove_one(struct pci_dev *pdev) /* For now keep DMA off. We can set it for all but A rev CS5510 once the core ATA code can handle it */ -static struct pci_device_id pata_cs5520[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, - { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520), }, - { 0, }, +static const struct pci_device_id pata_cs5520[] = { + { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, + { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5520), }, + + { }, }; static struct pci_driver cs5520_pci_driver = { @@ -312,7 +313,6 @@ static struct pci_driver cs5520_pci_driver = { .remove = cs5520_remove_one }; - static int __init cs5520_init(void) { return pci_register_driver(&cs5520_pci_driver); diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index 7bba4d954e..a07cc81ef7 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -353,13 +353,14 @@ fail_put: return -ENODEV; } -static struct pci_device_id cs5530[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, - { 0, }, +static const struct pci_device_id cs5530[] = { + { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, + + { }, }; static struct pci_driver cs5530_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = cs5530, .probe = cs5530_init_one, .remove = ata_pci_remove_one @@ -370,13 +371,11 @@ static int __init cs5530_init(void) return pci_register_driver(&cs5530_pci_driver); } - static void __exit cs5530_exit(void) { pci_unregister_driver(&cs5530_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Cyrix/NS/AMD 5530"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index d64fcdceaf..f8def3f9c6 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -257,9 +257,10 @@ static int cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, ports, 1); } -static struct pci_device_id cs5535[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, 0x002D), }, - { 0, }, +static const struct pci_device_id cs5535[] = { + { PCI_VDEVICE(NS, 0x002D), }, + + { }, }; static struct pci_driver cs5535_pci_driver = { @@ -274,13 +275,11 @@ static int __init cs5535_init(void) return pci_register_driver(&cs5535_pci_driver); } - static void __exit cs5535_exit(void) { pci_unregister_driver(&cs5535_pci_driver); } - MODULE_AUTHOR("Alan Cox, Jens Altmann, Wolfgan Zuleger, Alexander Kiausch"); MODULE_DESCRIPTION("low-level driver for the NS/AMD 5530"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c index dfa5ac5390..247b43608b 100644 --- a/drivers/ata/pata_cypress.c +++ b/drivers/ata/pata_cypress.c @@ -184,8 +184,8 @@ static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *i }; static struct ata_port_info *port_info[1] = { &info }; - /* Devfn 1 is the ATA primary. The secondary is magic and on devfn2. For the - moment we don't handle the secondary. FIXME */ + /* Devfn 1 is the ATA primary. The secondary is magic and on devfn2. + For the moment we don't handle the secondary. FIXME */ if (PCI_FUNC(pdev->devfn) != 1) return -ENODEV; @@ -193,13 +193,14 @@ static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *i return ata_pci_init_one(pdev, port_info, 1); } -static struct pci_device_id cy82c693[] = { - { PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { 0, }, +static const struct pci_device_id cy82c693[] = { + { PCI_VDEVICE(CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693), }, + + { }, }; static struct pci_driver cy82c693_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = cy82c693, .probe = cy82c693_init_one, .remove = ata_pci_remove_one diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index 95cd1ca181..ef18c60fe1 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -305,7 +305,8 @@ static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } static const struct pci_device_id efar_pci_tbl[] = { - { 0x1055, 0x9130, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VDEVICE(EFAR, 0x9130), }, + { } /* terminate list */ }; @@ -326,7 +327,6 @@ static void __exit efar_exit(void) pci_unregister_driver(&efar_pci_driver); } - module_init(efar_init); module_exit(efar_exit); diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index 8c757438f3..6d3e4c0f15 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -444,13 +444,14 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static struct pci_device_id hpt36x[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366), }, - { 0, }, +static const struct pci_device_id hpt36x[] = { + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, + + { }, }; static struct pci_driver hpt36x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt36x, .probe = hpt36x_init_one, .remove = ata_pci_remove_one diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index 10318c0012..7350443948 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -1219,17 +1219,18 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static struct pci_device_id hpt37x[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302), }, - { 0, }, +static const struct pci_device_id hpt37x[] = { + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT374), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), }, + + { }, }; static struct pci_driver hpt37x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt37x, .probe = hpt37x_init_one, .remove = ata_pci_remove_one @@ -1240,13 +1241,11 @@ static int __init hpt37x_init(void) return pci_register_driver(&hpt37x_pci_driver); } - static void __exit hpt37x_exit(void) { pci_unregister_driver(&hpt37x_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT37x/30x"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 5c5d4f6ab9..58cfb2bc80 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -560,16 +560,17 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static struct pci_device_id hpt3x2n[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302), }, - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372N), }, - { 0, }, +static const struct pci_device_id hpt3x2n[] = { + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), }, + + { }, }; static struct pci_driver hpt3x2n_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt3x2n, .probe = hpt3x2n_init_one, .remove = ata_pci_remove_one @@ -580,13 +581,11 @@ static int __init hpt3x2n_init(void) return pci_register_driver(&hpt3x2n_pci_driver); } - static void __exit hpt3x2n_exit(void) { pci_unregister_driver(&hpt3x2n_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT3x2n/30x"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index 1f084ab1cc..3334d72e25 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -192,13 +192,14 @@ static int hpt3x3_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static struct pci_device_id hpt3x3[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343), }, - { 0, }, +static const struct pci_device_id hpt3x3[] = { + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT343), }, + + { }, }; static struct pci_driver hpt3x3_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt3x3, .probe = hpt3x3_init_one, .remove = ata_pci_remove_one diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 82a46ff400..18ff3e59a8 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -808,14 +808,15 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } -static struct pci_device_id it821x[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8211), }, - { PCI_DEVICE(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212), }, - { 0, }, +static const struct pci_device_id it821x[] = { + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), }, + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), }, + + { }, }; static struct pci_driver it821x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = it821x, .probe = it821x_init_one, .remove = ata_pci_remove_one @@ -826,13 +827,11 @@ static int __init it821x_init(void) return pci_register_driver(&it821x_pci_driver); } - static void __exit it821x_exit(void) { pci_unregister_driver(&it821x_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the IT8211/IT8212 IDE RAID controller"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c index be3a866b11..52a2bdf3c3 100644 --- a/drivers/ata/pata_jmicron.c +++ b/drivers/ata/pata_jmicron.c @@ -229,11 +229,12 @@ static int jmicron_init_one (struct pci_dev *pdev, const struct pci_device_id *i } static const struct pci_device_id jmicron_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361), 361}, - { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363), 363}, - { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365), 365}, - { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366), 366}, - { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368), 368}, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB361), 361}, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB363), 363}, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB365), 365}, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB366), 366}, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB368), 368}, + { } /* terminate list */ }; diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index 3c65393c1f..9dfe3e9abe 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -274,11 +274,10 @@ static void __devexit mpiix_remove_one(struct pci_dev *pdev) dev_set_drvdata(dev, NULL); } - - static const struct pci_device_id mpiix[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX), }, - { 0, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), }, + + { }, }; static struct pci_driver mpiix_pci_driver = { @@ -293,13 +292,11 @@ static int __init mpiix_init(void) return pci_register_driver(&mpiix_pci_driver); } - static void __exit mpiix_exit(void) { pci_unregister_driver(&mpiix_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Intel MPIIX"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c index 76eb9c90be..f5672de99c 100644 --- a/drivers/ata/pata_netcell.c +++ b/drivers/ata/pata_netcell.c @@ -142,7 +142,8 @@ static int netcell_init_one (struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id netcell_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NETCELL, PCI_DEVICE_ID_REVOLUTION), }, + { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), }, + { } /* terminate list */ }; diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c index 2005a95f48..2a3dbeed89 100644 --- a/drivers/ata/pata_ns87410.c +++ b/drivers/ata/pata_ns87410.c @@ -200,12 +200,13 @@ static int ns87410_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id ns87410[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410), }, - { 0, }, + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87410), }, + + { }, }; static struct pci_driver ns87410_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = ns87410, .probe = ns87410_init_one, .remove = ata_pci_remove_one @@ -216,13 +217,11 @@ static int __init ns87410_init(void) return pci_register_driver(&ns87410_pci_driver); } - static void __exit ns87410_exit(void) { pci_unregister_driver(&ns87410_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Nat Semi 87410"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index 31a285ca88..fc947dfecd 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -303,7 +303,8 @@ static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id oldpiix_pci_tbl[] = { - { PCI_DEVICE(0x8086, 0x1230), }, + { PCI_VDEVICE(INTEL, 0x1230), }, + { } /* terminate list */ }; @@ -324,7 +325,6 @@ static void __exit oldpiix_exit(void) pci_unregister_driver(&oldpiix_pci_driver); } - module_init(oldpiix_init); module_exit(oldpiix_exit); diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c index 57fe21f3a9..a7320ba155 100644 --- a/drivers/ata/pata_opti.c +++ b/drivers/ata/pata_opti.c @@ -256,13 +256,14 @@ static int opti_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id opti[] = { - { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { 0, }, + { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C621), 0 }, + { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C825), 1 }, + + { }, }; static struct pci_driver opti_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = opti, .probe = opti_init_one, .remove = ata_pci_remove_one @@ -273,7 +274,6 @@ static int __init opti_init(void) return pci_register_driver(&opti_pci_driver); } - static void __exit opti_exit(void) { pci_unregister_driver(&opti_pci_driver); diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c index 7296a20cd1..c6906b4215 100644 --- a/drivers/ata/pata_optidma.c +++ b/drivers/ata/pata_optidma.c @@ -512,12 +512,13 @@ static int optidma_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id optidma[] = { - { PCI_DEVICE(0x1045, 0xD568), }, /* Opti 82C700 */ - { 0, }, + { PCI_VDEVICE(OPTI, 0xD568), }, /* Opti 82C700 */ + + { }, }; static struct pci_driver optidma_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = optidma, .probe = optidma_init_one, .remove = ata_pci_remove_one @@ -528,13 +529,11 @@ static int __init optidma_init(void) return pci_register_driver(&optidma_pci_driver); } - static void __exit optidma_exit(void) { pci_unregister_driver(&optidma_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Opti Firestar/Firestar Plus"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index cb501e145a..e93ea2702c 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -42,7 +42,7 @@ #define DRV_NAME "pata_pcmcia" -#define DRV_VERSION "0.2.9" +#define DRV_VERSION "0.2.11" /* * Private data structure to glue stuff together @@ -355,6 +355,8 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d), PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6), PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003), + PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852), PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index bd4ed6734e..d894d9918b 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -108,13 +108,14 @@ static struct pdc2027x_udma_timing { }; static const struct pci_device_id pdc2027x_pci_tbl[] = { - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_100 }, - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20270, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_100 }, - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, - { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20268), PDC_UDMA_100 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20269), PDC_UDMA_133 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), PDC_UDMA_100 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), PDC_UDMA_133 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), PDC_UDMA_133 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), PDC_UDMA_133 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), PDC_UDMA_133 }, + { } /* terminate list */ }; diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c index 48f4343276..5ba9eb20a6 100644 --- a/drivers/ata/pata_pdc202xx_old.c +++ b/drivers/ata/pata_pdc202xx_old.c @@ -385,17 +385,18 @@ static int pdc_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static struct pci_device_id pdc[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0}, - { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1}, - { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1}, - { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2}, - { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2}, - { 0, }, +static const struct pci_device_id pdc[] = { + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2 }, + + { }, }; static struct pci_driver pdc_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = pdc, .probe = pdc_init_one, .remove = ata_pci_remove_one @@ -406,13 +407,11 @@ static int __init pdc_init(void) return pci_register_driver(&pdc_pci_driver); } - static void __exit pdc_exit(void) { pci_unregister_driver(&pdc_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Promise 2024x and 20262-20267"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index c20bcf43ed..1af83d7694 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -300,7 +300,8 @@ static int radisys_init_one (struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id radisys_pci_tbl[] = { - { 0x1331, 0x8201, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VDEVICE(RADISYS, 0x8201), }, + { } /* terminate list */ }; @@ -321,7 +322,6 @@ static void __exit radisys_exit(void) pci_unregister_driver(&radisys_pci_driver); } - module_init(radisys_init); module_exit(radisys_exit); diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c index eccc6fd450..4533b6357d 100644 --- a/drivers/ata/pata_rz1000.c +++ b/drivers/ata/pata_rz1000.c @@ -170,20 +170,20 @@ fail: return -ENODEV; } -static struct pci_device_id pata_rz1000[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), }, - { PCI_DEVICE(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001), }, - { 0, }, +static const struct pci_device_id pata_rz1000[] = { + { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), }, + { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001), }, + + { }, }; static struct pci_driver rz1000_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = pata_rz1000, .probe = rz1000_init_one, .remove = ata_pci_remove_one }; - static int __init rz1000_init(void) { return pci_register_driver(&rz1000_pci_driver); diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index 107e6cd3dc..067d9d223e 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -253,13 +253,14 @@ static int sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 1); } -static struct pci_device_id sc1200[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_IDE), }, - { 0, }, +static const struct pci_device_id sc1200[] = { + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SCx200_IDE), }, + + { }, }; static struct pci_driver sc1200_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = sc1200, .probe = sc1200_init_one, .remove = ata_pci_remove_one @@ -270,13 +271,11 @@ static int __init sc1200_init(void) return pci_register_driver(&sc1200_pci_driver); } - static void __exit sc1200_exit(void) { pci_unregister_driver(&sc1200_pci_driver); } - MODULE_AUTHOR("Alan Cox, Mark Lord"); MODULE_DESCRIPTION("low-level driver for the NS/AMD SC1200"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index a5c8d7e121..5bbf76ec14 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -553,13 +553,14 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id return ata_pci_init_one(pdev, port_info, ports); } -static struct pci_device_id serverworks[] = { - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, - { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, - { 0, }, +static const struct pci_device_id serverworks[] = { + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, + + { }, }; static struct pci_driver serverworks_pci_driver = { @@ -574,13 +575,11 @@ static int __init serverworks_init(void) return pci_register_driver(&serverworks_pci_driver); } - static void __exit serverworks_exit(void) { pci_unregister_driver(&serverworks_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index c8b2e26db7..4a2b72b4be 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -348,12 +348,13 @@ static int sil680_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct pci_device_id sil680[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_680), }, - { 0, }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), }, + + { }, }; static struct pci_driver sil680_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = sil680, .probe = sil680_init_one, .remove = ata_pci_remove_one @@ -364,13 +365,11 @@ static int __init sil680_init(void) return pci_register_driver(&sil680_pci_driver); } - static void __exit sil680_exit(void) { pci_unregister_driver(&sil680_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for SI680 PATA"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 17791e2785..b9ffafb419 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -988,8 +988,9 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } static const struct pci_device_id sis_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x5513), }, /* SiS 5513 */ - { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x5518), }, /* SiS 5518 */ + { PCI_VDEVICE(SI, 0x5513), }, /* SiS 5513 */ + { PCI_VDEVICE(SI, 0x5518), }, /* SiS 5518 */ + { } }; @@ -1010,7 +1011,6 @@ static void __exit sis_exit(void) pci_unregister_driver(&sis_pci_driver); } - module_init(sis_init); module_exit(sis_exit); diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 5b762acc56..08a6dc8867 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -351,9 +351,10 @@ static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id return ata_pci_init_one(dev, port_info, 1); /* For now */ } -static struct pci_device_id sl82c105[] = { - { PCI_DEVICE(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105), }, - { 0, }, +static const struct pci_device_id sl82c105[] = { + { PCI_VDEVICE(WINBOND, PCI_DEVICE_ID_WINBOND_82C105), }, + + { }, }; static struct pci_driver sl82c105_pci_driver = { @@ -368,13 +369,11 @@ static int __init sl82c105_init(void) return pci_register_driver(&sl82c105_pci_driver); } - static void __exit sl82c105_exit(void) { pci_unregister_driver(&sl82c105_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Sl82c105"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c index a954ed93a4..9640f80e8b 100644 --- a/drivers/ata/pata_triflex.c +++ b/drivers/ata/pata_triflex.c @@ -248,13 +248,13 @@ static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id triflex[] = { - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE), }, + + { }, }; static struct pci_driver triflex_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = triflex, .probe = triflex_init_one, .remove = ata_pci_remove_one @@ -265,13 +265,11 @@ static int __init triflex_init(void) return pci_register_driver(&triflex_pci_driver); } - static void __exit triflex_exit(void) { pci_unregister_driver(&triflex_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Compaq Triflex"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 7b5dd2343b..1e7be9eee9 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -529,15 +529,16 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct pci_device_id via[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1), }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1), }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_6410), }, - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), }, - { 0, }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1), }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C586_1), }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410), }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), }, + + { }, }; static struct pci_driver via_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = via, .probe = via_init_one, .remove = ata_pci_remove_one @@ -548,13 +549,11 @@ static int __init via_init(void) return pci_register_driver(&via_pci_driver); } - static void __exit via_exit(void) { pci_unregister_driver(&via_pci_driver); } - MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for VIA PATA"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 0e23ecb77b..81f3d219e7 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -192,8 +192,7 @@ static struct ata_port_info adma_port_info[] = { }; static const struct pci_device_id adma_ata_pci_tbl[] = { - { PCI_VENDOR_ID_PDC, 0x1841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_1841_idx }, + { PCI_VDEVICE(PDC, 0x1841), board_1841_idx }, { } /* terminate list */ }; diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index c01496df4a..e6aa1a86d5 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -533,19 +533,20 @@ static const struct ata_port_info mv_port_info[] = { }; static const struct pci_device_id mv_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_5080}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x}, - - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6042), 0, 0, chip_6042}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x}, - {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x}, - - {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x0241), 0, 0, chip_604x}, - {} /* terminate list */ + { PCI_VDEVICE(MARVELL, 0x5040), chip_504x }, + { PCI_VDEVICE(MARVELL, 0x5041), chip_504x }, + { PCI_VDEVICE(MARVELL, 0x5080), chip_5080 }, + { PCI_VDEVICE(MARVELL, 0x5081), chip_508x }, + + { PCI_VDEVICE(MARVELL, 0x6040), chip_604x }, + { PCI_VDEVICE(MARVELL, 0x6041), chip_604x }, + { PCI_VDEVICE(MARVELL, 0x6042), chip_6042 }, + { PCI_VDEVICE(MARVELL, 0x6080), chip_608x }, + { PCI_VDEVICE(MARVELL, 0x6081), chip_608x }, + + { PCI_VDEVICE(ADAPTEC2, 0x0241), chip_604x }, + + { } /* terminate list */ }; static struct pci_driver mv_pci_driver = { diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 8cd730fe5d..d09d20a177 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -106,45 +106,32 @@ enum nv_host_type }; static const struct pci_device_id nv_pci_tbl[] = { - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, - { PCI_VENDOR_ID_NVIDIA, 0x045f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), NFORCE2 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), NFORCE3 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), NFORCE3 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA), CK804 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2), CK804 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA), CK804 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2), CK804 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), GENERIC }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), GENERIC }, + { PCI_VDEVICE(NVIDIA, 0x045c), GENERIC }, + { PCI_VDEVICE(NVIDIA, 0x045d), GENERIC }, + { PCI_VDEVICE(NVIDIA, 0x045e), GENERIC }, + { PCI_VDEVICE(NVIDIA, 0x045f), GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC }, - { 0, } /* terminate list */ + + { } /* terminate list */ }; static struct pci_driver nv_pci_driver = { diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index d627812ea7..15c9437710 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -234,48 +234,31 @@ static const struct ata_port_info pdc_port_info[] = { }; static const struct pci_device_id pdc_ata_pci_tbl[] = { - { PCI_VENDOR_ID_PROMISE, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3571, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3373, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3375, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3376, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - { PCI_VENDOR_ID_PROMISE, 0x3574, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2057x }, - { PCI_VENDOR_ID_PROMISE, 0x3d75, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2057x }, - { PCI_VENDOR_ID_PROMISE, 0x3d73, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2037x }, - - { PCI_VENDOR_ID_PROMISE, 0x3318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3d17, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20319 }, - { PCI_VENDOR_ID_PROMISE, 0x3d18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_40518 }, - - { PCI_VENDOR_ID_PROMISE, 0x6629, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20619 }, + { PCI_VDEVICE(PROMISE, 0x3371), board_2037x }, + { PCI_VDEVICE(PROMISE, 0x3570), board_2037x }, + { PCI_VDEVICE(PROMISE, 0x3571), board_2037x }, + { PCI_VDEVICE(PROMISE, 0x3373), board_2037x }, + { PCI_VDEVICE(PROMISE, 0x3375), board_2037x }, + { PCI_VDEVICE(PROMISE, 0x3376), board_2037x }, + { PCI_VDEVICE(PROMISE, 0x3574), board_2057x }, + { PCI_VDEVICE(PROMISE, 0x3d75), board_2057x }, + { PCI_VDEVICE(PROMISE, 0x3d73), board_2037x }, + + { PCI_VDEVICE(PROMISE, 0x3318), board_20319 }, + { PCI_VDEVICE(PROMISE, 0x3319), board_20319 }, + { PCI_VDEVICE(PROMISE, 0x3515), board_20319 }, + { PCI_VDEVICE(PROMISE, 0x3519), board_20319 }, + { PCI_VDEVICE(PROMISE, 0x3d17), board_20319 }, + { PCI_VDEVICE(PROMISE, 0x3d18), board_40518 }, + + { PCI_VDEVICE(PROMISE, 0x6629), board_20619 }, /* TODO: remove all associated board_20771 code, as it completely * duplicates board_2037x code, unless reason for separation can be * divined. */ #if 0 - { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20771 }, + { PCI_VDEVICE(PROMISE, 0x3570), board_20771 }, #endif { } /* terminate list */ diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index fa29dfe2a7..7f6cc3c07d 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -185,8 +185,7 @@ static const struct ata_port_info qs_port_info[] = { }; static const struct pci_device_id qs_ata_pci_tbl[] = { - { PCI_VENDOR_ID_PDC, 0x2068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_2068_idx }, + { PCI_VDEVICE(PDC, 0x2068), board_2068_idx }, { } /* terminate list */ }; diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index c63dbabc0c..3d9fa1cc83 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -123,13 +123,14 @@ static void sil_thaw(struct ata_port *ap); static const struct pci_device_id sil_pci_tbl[] = { - { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3512 }, - { 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 }, - { 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq }, - { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq }, + { PCI_VDEVICE(CMD, 0x3112), sil_3112 }, + { PCI_VDEVICE(CMD, 0x0240), sil_3112 }, + { PCI_VDEVICE(CMD, 0x3512), sil_3512 }, + { PCI_VDEVICE(CMD, 0x3114), sil_3114 }, + { PCI_VDEVICE(ATI, 0x436e), sil_3112 }, + { PCI_VDEVICE(ATI, 0x4379), sil_3112_no_sata_irq }, + { PCI_VDEVICE(ATI, 0x437a), sil_3112_no_sata_irq }, + { } /* terminate list */ }; diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 39cb07baeb..a951f40c2f 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -344,11 +344,12 @@ static int sil24_pci_device_resume(struct pci_dev *pdev); #endif static const struct pci_device_id sil24_pci_tbl[] = { - { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, - { 0x8086, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, - { 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 }, - { 0x1095, 0x3131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, - { 0x1095, 0x3531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, + { PCI_VDEVICE(CMD, 0x3124), BID_SIL3124 }, + { PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 }, + { PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 }, + { PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 }, + { PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 }, + { } /* terminate list */ }; diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 18d49fff8d..0738f52463 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -67,13 +67,13 @@ static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg); static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static const struct pci_device_id sis_pci_tbl[] = { - { PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, - { PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, - { PCI_VENDOR_ID_SI, 0x182, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, + { PCI_VDEVICE(SI, 0x180), sis_180 }, + { PCI_VDEVICE(SI, 0x181), sis_180 }, + { PCI_VDEVICE(SI, 0x182), sis_180 }, + { } /* terminate list */ }; - static struct pci_driver sis_pci_driver = { .name = DRV_NAME, .id_table = sis_pci_tbl, diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index d6d6658d83..84025a2fd5 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -469,15 +469,15 @@ err_out: * controller * */ static const struct pci_device_id k2_sata_pci_tbl[] = { - { 0x1166, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x1166, 0x0241, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x1166, 0x0242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { 0x1166, 0x024a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x1166, 0x024b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { PCI_VDEVICE(SERVERWORKS, 0x0240), 4 }, + { PCI_VDEVICE(SERVERWORKS, 0x0241), 4 }, + { PCI_VDEVICE(SERVERWORKS, 0x0242), 8 }, + { PCI_VDEVICE(SERVERWORKS, 0x024a), 4 }, + { PCI_VDEVICE(SERVERWORKS, 0x024b), 4 }, + { } }; - static struct pci_driver k2_sata_pci_driver = { .name = DRV_NAME, .id_table = k2_sata_pci_tbl, @@ -485,19 +485,16 @@ static struct pci_driver k2_sata_pci_driver = { .remove = ata_pci_remove_one, }; - static int __init k2_sata_init(void) { return pci_register_driver(&k2_sata_pci_driver); } - static void __exit k2_sata_exit(void) { pci_unregister_driver(&k2_sata_pci_driver); } - MODULE_AUTHOR("Benjamin Herrenschmidt"); MODULE_DESCRIPTION("low-level driver for K2 SATA controller"); MODULE_LICENSE("GPL"); diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 091867e10e..8c74f2ff43 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -230,12 +230,11 @@ static const struct ata_port_info pdc_port_info[] = { }; static const struct pci_device_id pdc_sata_pci_tbl[] = { - { PCI_VENDOR_ID_PROMISE, 0x6622, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - board_20621 }, + { PCI_VDEVICE(PROMISE, 0x6622), board_20621 }, + { } /* terminate list */ }; - static struct pci_driver pdc_sata_pci_driver = { .name = DRV_NAME, .id_table = pdc_sata_pci_tbl, diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index dd76f37be1..5c603ca3a5 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -61,13 +61,13 @@ static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static const struct pci_device_id uli_pci_tbl[] = { - { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 }, - { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 }, - { PCI_VENDOR_ID_AL, 0x5281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5281 }, + { PCI_VDEVICE(AL, 0x5289), uli_5289 }, + { PCI_VDEVICE(AL, 0x5287), uli_5287 }, + { PCI_VDEVICE(AL, 0x5281), uli_5281 }, + { } /* terminate list */ }; - static struct pci_driver uli_pci_driver = { .name = DRV_NAME, .id_table = uli_pci_tbl, diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index a72a2389a1..f4455a1efe 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -77,9 +77,9 @@ static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void vt6420_error_handler(struct ata_port *ap); static const struct pci_device_id svia_pci_tbl[] = { - { 0x1106, 0x0591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, - { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, - { 0x1106, 0x3249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6421 }, + { PCI_VDEVICE(VIA, 0x0591), vt6420 }, + { PCI_VDEVICE(VIA, 0x3149), vt6420 }, + { PCI_VDEVICE(VIA, 0x3249), vt6421 }, { } /* terminate list */ }; diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index d0d92f33de..273d88fcf9 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -442,16 +442,15 @@ err_out: return rc; } - static const struct pci_device_id vsc_sata_pci_tbl[] = { { PCI_VENDOR_ID_VITESSE, 0x7174, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, { PCI_VENDOR_ID_INTEL, 0x3200, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, + { } /* terminate list */ }; - static struct pci_driver vsc_sata_pci_driver = { .name = DRV_NAME, .id_table = vsc_sata_pci_tbl, @@ -459,19 +458,16 @@ static struct pci_driver vsc_sata_pci_driver = { .remove = ata_pci_remove_one, }; - static int __init vsc_sata_init(void) { return pci_register_driver(&vsc_sata_pci_driver); } - static void __exit vsc_sata_exit(void) { pci_unregister_driver(&vsc_sata_pci_driver); } - MODULE_AUTHOR("Jeremy Higdon"); MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller"); MODULE_LICENSE("GPL"); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index a6b2aa67c9..f2904f67af 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -62,6 +62,8 @@ #include +#define DRIVER_NAME "pktcdvd" + #if PACKET_DEBUG #define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) #else @@ -80,7 +82,7 @@ static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; static struct proc_dir_entry *pkt_proc; -static int pkt_major; +static int pktdev_major; static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ static mempool_t *psd_pool; @@ -89,7 +91,7 @@ static void pkt_bio_finished(struct pktcdvd_device *pd) { BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0); if (atomic_dec_and_test(&pd->cdrw.pending_bios)) { - VPRINTK("pktcdvd: queue empty\n"); + VPRINTK(DRIVER_NAME": queue empty\n"); atomic_set(&pd->iosched.attention, 1); wake_up(&pd->wqueue); } @@ -400,7 +402,7 @@ static void pkt_dump_sense(struct packet_command *cgc) int i; struct request_sense *sense = cgc->sense; - printk("pktcdvd:"); + printk(DRIVER_NAME":"); for (i = 0; i < CDROM_PACKET_SIZE; i++) printk(" %02x", cgc->cmd[i]); printk(" - "); @@ -528,7 +530,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) need_write_seek = 0; if (need_write_seek && reads_queued) { if (atomic_read(&pd->cdrw.pending_bios) > 0) { - VPRINTK("pktcdvd: write, waiting\n"); + VPRINTK(DRIVER_NAME": write, waiting\n"); break; } pkt_flush_cache(pd); @@ -537,7 +539,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) } else { if (!reads_queued && writes_queued) { if (atomic_read(&pd->cdrw.pending_bios) > 0) { - VPRINTK("pktcdvd: read, waiting\n"); + VPRINTK(DRIVER_NAME": read, waiting\n"); break; } pd->iosched.writing = 1; @@ -600,7 +602,7 @@ static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q set_bit(PACKET_MERGE_SEGS, &pd->flags); return 0; } else { - printk("pktcdvd: cdrom max_phys_segments too small\n"); + printk(DRIVER_NAME": cdrom max_phys_segments too small\n"); return -EIO; } } @@ -1049,7 +1051,7 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt) for (f = 0; f < pkt->frames; f++) if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset)) BUG(); - VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt); + VPRINTK(DRIVER_NAME": vcnt=%d\n", pkt->w_bio->bi_vcnt); atomic_set(&pkt->io_wait, 1); pkt->w_bio->bi_rw = WRITE; @@ -1286,7 +1288,7 @@ work_to_do: static void pkt_print_settings(struct pktcdvd_device *pd) { - printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); + printk(DRIVER_NAME": %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); printk("%u blocks, ", pd->settings.size >> 2); printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2'); } @@ -1471,7 +1473,7 @@ static int pkt_set_write_settings(struct pktcdvd_device *pd) /* * paranoia */ - printk("pktcdvd: write mode wrong %d\n", wp->data_block_type); + printk(DRIVER_NAME": write mode wrong %d\n", wp->data_block_type); return 1; } wp->packet_size = cpu_to_be32(pd->settings.size >> 2); @@ -1515,7 +1517,7 @@ static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti) if (ti->rt == 1 && ti->blank == 0) return 1; - printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); + printk(DRIVER_NAME": bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); return 0; } @@ -1533,7 +1535,7 @@ static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di) case 0x12: /* DVD-RAM */ return 1; default: - VPRINTK("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile); + VPRINTK(DRIVER_NAME": Wrong disc profile (%x)\n", pd->mmc3_profile); return 0; } @@ -1542,22 +1544,22 @@ static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di) * but i'm not sure, should we leave this to user apps? probably. */ if (di->disc_type == 0xff) { - printk("pktcdvd: Unknown disc. No track?\n"); + printk(DRIVER_NAME": Unknown disc. No track?\n"); return 0; } if (di->disc_type != 0x20 && di->disc_type != 0) { - printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type); + printk(DRIVER_NAME": Wrong disc type (%x)\n", di->disc_type); return 0; } if (di->erasable == 0) { - printk("pktcdvd: Disc not erasable\n"); + printk(DRIVER_NAME": Disc not erasable\n"); return 0; } if (di->border_status == PACKET_SESSION_RESERVED) { - printk("pktcdvd: Can't write to last track (reserved)\n"); + printk(DRIVER_NAME": Can't write to last track (reserved)\n"); return 0; } @@ -1593,12 +1595,12 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ if ((ret = pkt_get_track_info(pd, track, 1, &ti))) { - printk("pktcdvd: failed get_track\n"); + printk(DRIVER_NAME": failed get_track\n"); return ret; } if (!pkt_writable_track(pd, &ti)) { - printk("pktcdvd: can't write to this track\n"); + printk(DRIVER_NAME": can't write to this track\n"); return -EROFS; } @@ -1608,11 +1610,11 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) */ pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; if (pd->settings.size == 0) { - printk("pktcdvd: detected zero packet size!\n"); + printk(DRIVER_NAME": detected zero packet size!\n"); return -ENXIO; } if (pd->settings.size > PACKET_MAX_SECTORS) { - printk("pktcdvd: packet size is too big\n"); + printk(DRIVER_NAME": packet size is too big\n"); return -EROFS; } pd->settings.fp = ti.fp; @@ -1654,7 +1656,7 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) pd->settings.block_mode = PACKET_BLOCK_MODE2; break; default: - printk("pktcdvd: unknown data mode\n"); + printk(DRIVER_NAME": unknown data mode\n"); return -EROFS; } return 0; @@ -1688,10 +1690,10 @@ static int pkt_write_caching(struct pktcdvd_device *pd, int set) cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff)); ret = pkt_mode_select(pd, &cgc); if (ret) { - printk("pktcdvd: write caching control failed\n"); + printk(DRIVER_NAME": write caching control failed\n"); pkt_dump_sense(&cgc); } else if (!ret && set) - printk("pktcdvd: enabled write caching on %s\n", pd->name); + printk(DRIVER_NAME": enabled write caching on %s\n", pd->name); return ret; } @@ -1805,11 +1807,11 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) } if (!buf[6] & 0x40) { - printk("pktcdvd: Disc type is not CD-RW\n"); + printk(DRIVER_NAME": Disc type is not CD-RW\n"); return 1; } if (!buf[6] & 0x4) { - printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n"); + printk(DRIVER_NAME": A1 values on media are not valid, maybe not CDRW?\n"); return 1; } @@ -1829,14 +1831,14 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) *speed = us_clv_to_speed[sp]; break; default: - printk("pktcdvd: Unknown disc sub-type %d\n",st); + printk(DRIVER_NAME": Unknown disc sub-type %d\n",st); return 1; } if (*speed) { - printk("pktcdvd: Max. media speed: %d\n",*speed); + printk(DRIVER_NAME": Max. media speed: %d\n",*speed); return 0; } else { - printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st); + printk(DRIVER_NAME": Unknown speed %d for sub-type %d\n",sp,st); return 1; } } @@ -1847,7 +1849,7 @@ static int pkt_perform_opc(struct pktcdvd_device *pd) struct request_sense sense; int ret; - VPRINTK("pktcdvd: Performing OPC\n"); + VPRINTK(DRIVER_NAME": Performing OPC\n"); init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); cgc.sense = &sense; @@ -1865,12 +1867,12 @@ static int pkt_open_write(struct pktcdvd_device *pd) unsigned int write_speed, media_write_speed, read_speed; if ((ret = pkt_probe_settings(pd))) { - VPRINTK("pktcdvd: %s failed probe\n", pd->name); + VPRINTK(DRIVER_NAME": %s failed probe\n", pd->name); return ret; } if ((ret = pkt_set_write_settings(pd))) { - DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name); + DPRINTK(DRIVER_NAME": %s failed saving write settings\n", pd->name); return -EIO; } @@ -1882,26 +1884,26 @@ static int pkt_open_write(struct pktcdvd_device *pd) case 0x13: /* DVD-RW */ case 0x1a: /* DVD+RW */ case 0x12: /* DVD-RAM */ - DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed); + DPRINTK(DRIVER_NAME": write speed %ukB/s\n", write_speed); break; default: if ((ret = pkt_media_speed(pd, &media_write_speed))) media_write_speed = 16; write_speed = min(write_speed, media_write_speed * 177); - DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176); + DPRINTK(DRIVER_NAME": write speed %ux\n", write_speed / 176); break; } read_speed = write_speed; if ((ret = pkt_set_speed(pd, write_speed, read_speed))) { - DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name); + DPRINTK(DRIVER_NAME": %s couldn't set write speed\n", pd->name); return -EIO; } pd->write_speed = write_speed; pd->read_speed = read_speed; if ((ret = pkt_perform_opc(pd))) { - DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name); + DPRINTK(DRIVER_NAME": %s Optimum Power Calibration failed\n", pd->name); } return 0; @@ -1929,7 +1931,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write) goto out_putdev; if ((ret = pkt_get_last_written(pd, &lba))) { - printk("pktcdvd: pkt_get_last_written failed\n"); + printk(DRIVER_NAME": pkt_get_last_written failed\n"); goto out_unclaim; } @@ -1959,11 +1961,11 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write) if (write) { if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { - printk("pktcdvd: not enough memory for buffers\n"); + printk(DRIVER_NAME": not enough memory for buffers\n"); ret = -ENOMEM; goto out_unclaim; } - printk("pktcdvd: %lukB available on disc\n", lba << 1); + printk(DRIVER_NAME": %lukB available on disc\n", lba << 1); } return 0; @@ -1983,7 +1985,7 @@ out: static void pkt_release_dev(struct pktcdvd_device *pd, int flush) { if (flush && pkt_flush_cache(pd)) - DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); + DPRINTK(DRIVER_NAME": %s not flushing cache\n", pd->name); pkt_lock_door(pd, 0); @@ -2006,7 +2008,7 @@ static int pkt_open(struct inode *inode, struct file *file) struct pktcdvd_device *pd = NULL; int ret; - VPRINTK("pktcdvd: entering open\n"); + VPRINTK(DRIVER_NAME": entering open\n"); mutex_lock(&ctl_mutex); pd = pkt_find_dev_from_minor(iminor(inode)); @@ -2040,7 +2042,7 @@ static int pkt_open(struct inode *inode, struct file *file) out_dec: pd->refcnt--; out: - VPRINTK("pktcdvd: failed open (%d)\n", ret); + VPRINTK(DRIVER_NAME": failed open (%d)\n", ret); mutex_unlock(&ctl_mutex); return ret; } @@ -2088,7 +2090,7 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) pd = q->queuedata; if (!pd) { - printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); + printk(DRIVER_NAME": %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); goto end_io; } @@ -2110,13 +2112,13 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) } if (!test_bit(PACKET_WRITABLE, &pd->flags)) { - printk("pktcdvd: WRITE for ro device %s (%llu)\n", + printk(DRIVER_NAME": WRITE for ro device %s (%llu)\n", pd->name, (unsigned long long)bio->bi_sector); goto end_io; } if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) { - printk("pktcdvd: wrong bio size\n"); + printk(DRIVER_NAME": wrong bio size\n"); goto end_io; } @@ -2319,7 +2321,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) struct block_device *bdev; if (pd->pkt_dev == dev) { - printk("pktcdvd: Recursive setup not allowed\n"); + printk(DRIVER_NAME": Recursive setup not allowed\n"); return -EBUSY; } for (i = 0; i < MAX_WRITERS; i++) { @@ -2327,11 +2329,11 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) if (!pd2) continue; if (pd2->bdev->bd_dev == dev) { - printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b)); + printk(DRIVER_NAME": %s already setup\n", bdevname(pd2->bdev, b)); return -EBUSY; } if (pd2->pkt_dev == dev) { - printk("pktcdvd: Can't chain pktcdvd devices\n"); + printk(DRIVER_NAME": Can't chain pktcdvd devices\n"); return -EBUSY; } } @@ -2354,7 +2356,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) atomic_set(&pd->cdrw.pending_bios, 0); pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name); if (IS_ERR(pd->cdrw.thread)) { - printk("pktcdvd: can't start kernel thread\n"); + printk(DRIVER_NAME": can't start kernel thread\n"); ret = -ENOMEM; goto out_mem; } @@ -2364,7 +2366,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) proc->data = pd; proc->proc_fops = &pkt_proc_fops; } - DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b)); + DPRINTK(DRIVER_NAME": writer %s mapped to %s\n", pd->name, bdevname(bdev, b)); return 0; out_mem: @@ -2401,7 +2403,7 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); default: - VPRINTK("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); + VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd); return -ENOTTY; } @@ -2446,7 +2448,7 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) if (!pkt_devs[idx]) break; if (idx == MAX_WRITERS) { - printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + printk(DRIVER_NAME": max %d writers supported\n", MAX_WRITERS); return -EBUSY; } @@ -2470,15 +2472,15 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) spin_lock_init(&pd->lock); spin_lock_init(&pd->iosched.lock); - sprintf(pd->name, "pktcdvd%d", idx); + sprintf(pd->name, DRIVER_NAME"%d", idx); init_waitqueue_head(&pd->wqueue); pd->bio_queue = RB_ROOT; - disk->major = pkt_major; + disk->major = pktdev_major; disk->first_minor = idx; disk->fops = &pktcdvd_ops; disk->flags = GENHD_FL_REMOVABLE; - sprintf(disk->disk_name, "pktcdvd%d", idx); + sprintf(disk->disk_name, DRIVER_NAME"%d", idx); disk->private_data = pd; disk->queue = blk_alloc_queue(GFP_KERNEL); if (!disk->queue) @@ -2520,7 +2522,7 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) break; } if (idx == MAX_WRITERS) { - DPRINTK("pktcdvd: dev not setup\n"); + DPRINTK(DRIVER_NAME": dev not setup\n"); return -ENXIO; } @@ -2533,7 +2535,7 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) blkdev_put(pd->bdev); remove_proc_entry(pd->name, pkt_proc); - DPRINTK("pktcdvd: writer %s unmapped\n", pd->name); + DPRINTK(DRIVER_NAME": writer %s unmapped\n", pd->name); del_gendisk(pd->disk); blk_cleanup_queue(pd->disk->queue); @@ -2610,7 +2612,7 @@ static struct file_operations pkt_ctl_fops = { static struct miscdevice pkt_misc = { .minor = MISC_DYNAMIC_MINOR, - .name = "pktcdvd", + .name = DRIVER_NAME, .fops = &pkt_ctl_fops }; @@ -2623,28 +2625,28 @@ static int __init pkt_init(void) if (!psd_pool) return -ENOMEM; - ret = register_blkdev(pkt_major, "pktcdvd"); + ret = register_blkdev(pktdev_major, DRIVER_NAME); if (ret < 0) { - printk("pktcdvd: Unable to register block device\n"); + printk(DRIVER_NAME": Unable to register block device\n"); goto out2; } - if (!pkt_major) - pkt_major = ret; + if (!pktdev_major) + pktdev_major = ret; ret = misc_register(&pkt_misc); if (ret) { - printk("pktcdvd: Unable to register misc device\n"); + printk(DRIVER_NAME": Unable to register misc device\n"); goto out; } mutex_init(&ctl_mutex); - pkt_proc = proc_mkdir("pktcdvd", proc_root_driver); + pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver); return 0; out: - unregister_blkdev(pkt_major, "pktcdvd"); + unregister_blkdev(pktdev_major, DRIVER_NAME); out2: mempool_destroy(psd_pool); return ret; @@ -2652,9 +2654,9 @@ out2: static void __exit pkt_exit(void) { - remove_proc_entry("pktcdvd", proc_root_driver); + remove_proc_entry(DRIVER_NAME, proc_root_driver); misc_deregister(&pkt_misc); - unregister_blkdev(pkt_major, "pktcdvd"); + unregister_blkdev(pktdev_major, DRIVER_NAME); mempool_destroy(psd_pool); } diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index d0e92ed0a3..486f97c3f4 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -112,17 +112,6 @@ static struct serial_state rs_table[1]; #define NR_PORTS ARRAY_SIZE(rs_table) -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static unsigned char *tmp_buf; - #include #define serial_isroot() (capable(CAP_SYS_ADMIN)) @@ -912,7 +901,7 @@ static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count if (serial_paranoia_check(info, tty->name, "rs_write")) return 0; - if (!info->xmit.buf || !tmp_buf) + if (!info->xmit.buf) return 0; local_save_flags(flags); @@ -1778,7 +1767,6 @@ static int rs_open(struct tty_struct *tty, struct file * filp) { struct async_struct *info; int retval, line; - unsigned long page; line = tty->index; if ((line < 0) || (line >= NR_PORTS)) { @@ -1798,17 +1786,6 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #endif info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) { - return -ENOMEM; - } - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - } - /* * If the port is the middle of closing, bail out now */ @@ -2090,11 +2067,6 @@ static __exit void rs_exit(void) kfree(info); } - if (tmp_buf) { - free_page((unsigned long) tmp_buf); - tmp_buf = NULL; - } - release_mem_region(CUSTOM_PHYSADDR+0x30, 4); } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index f85b4eb166..87b2fb5108 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -747,18 +747,6 @@ static struct cyclades_port cy_port[NR_PORTS]; static int cy_next_channel; /* next minor available */ -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. This buffer is - * allocated when the first cy_open occurs. - */ -static unsigned char *tmp_buf; - /* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra @@ -2466,7 +2454,6 @@ cy_open(struct tty_struct *tty, struct file * filp) { struct cyclades_port *info; int retval, line; - unsigned long page; line = tty->index; if ((line < 0) || (NR_PORTS <= line)){ @@ -2545,15 +2532,6 @@ cy_open(struct tty_struct *tty, struct file * filp) printk("cyc:cy_open (%d): incrementing count to %d\n", current->pid, info->count); #endif - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - } /* * If the port is the middle of closing, bail out now @@ -2832,7 +2810,7 @@ cy_write(struct tty_struct * tty, const unsigned char *buf, int count) return 0; } - if (!info->xmit_buf || !tmp_buf) + if (!info->xmit_buf) return 0; CY_LOCK(info, flags); @@ -5490,10 +5468,6 @@ cy_cleanup_module(void) #endif } } - if (tmp_buf) { - free_page((unsigned long) tmp_buf); - tmp_buf = NULL; - } } /* cy_cleanup_module */ module_init(cy_init); diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 3baa2ab8cb..c3f95583a1 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -1113,11 +1113,8 @@ static void __exit epca_module_exit(void) ch = card_ptr[crd]; for (count = 0; count < bd->numports; count++, ch++) { /* Begin for each port */ - if (ch) { - if (ch->tty) - tty_hangup(ch->tty); - kfree(ch->tmp_buf); - } + if (ch && ch->tty) + tty_hangup(ch->tty); } /* End for each port */ } /* End for each card */ pci_unregister_driver (&epca_driver); @@ -1635,16 +1632,6 @@ static void post_fep_init(unsigned int crd) init_waitqueue_head(&ch->close_wait); spin_unlock_irqrestore(&epca_lock, flags); - - ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); - if (!ch->tmp_buf) { - printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); - release_region((int)bd->port, 4); - while(i-- > 0) - kfree((ch--)->tmp_buf); - return; - } else - memset((void *)ch->tmp_buf,0,ch->txbufsize); } /* End for each port */ printk(KERN_INFO diff --git a/drivers/char/epca.h b/drivers/char/epca.h index 456d6c8f94..a297238cd3 100644 --- a/drivers/char/epca.h +++ b/drivers/char/epca.h @@ -130,7 +130,6 @@ struct channel unsigned long c_oflag; unsigned char __iomem *txptr; unsigned char __iomem *rxptr; - unsigned char *tmp_buf; struct board_info *board; struct board_chan __iomem *brdchan; struct digi_struct digiext; diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 4711d9b3a5..87127e49c0 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -33,8 +33,6 @@ #define DEBUG -static char * tmp_buf; - static int gs_debug; #ifdef DEBUG @@ -205,7 +203,7 @@ int gs_write(struct tty_struct * tty, if (!tty) return -EIO; port = tty->driver_data; - if (!port || !port->xmit_buf || !tmp_buf) + if (!port || !port->xmit_buf) return -EIO; local_save_flags(flags); @@ -837,24 +835,9 @@ void gs_set_termios (struct tty_struct * tty, int gs_init_port(struct gs_port *port) { unsigned long flags; - unsigned long page; func_enter (); - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - spin_lock_irqsave (&port->driver_lock, flags); /* Don't expect this to make a difference. */ - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - spin_unlock_irqrestore (&port->driver_lock, flags); - if (!tmp_buf) { - func_exit (); - return -ENOMEM; - } - } - if (port->flags & ASYNC_INITIALIZED) { func_exit (); return 0; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 214d850112..b0ab3f28cc 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -81,7 +81,6 @@ static struct riscom_board * IRQ_to_board[16]; static struct tty_driver *riscom_driver; -static unsigned char * tmp_buf; static unsigned long baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, @@ -1124,7 +1123,7 @@ static int rc_write(struct tty_struct * tty, bp = port_Board(port); - if (!tty || !port->xmit_buf || !tmp_buf) + if (!tty || !port->xmit_buf) return 0; save_flags(flags); @@ -1612,11 +1611,6 @@ static inline int rc_init_drivers(void) if (!riscom_driver) return -ENOMEM; - if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) { - printk(KERN_ERR "rc: Couldn't get free page.\n"); - put_tty_driver(riscom_driver); - return 1; - } memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); riscom_driver->owner = THIS_MODULE; riscom_driver->name = "ttyL"; @@ -1629,7 +1623,6 @@ static inline int rc_init_drivers(void) riscom_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(riscom_driver, &riscom_ops); if ((error = tty_register_driver(riscom_driver))) { - free_page((unsigned long)tmp_buf); put_tty_driver(riscom_driver); printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, " "error = %d\n", @@ -1657,7 +1650,6 @@ static void rc_release_drivers(void) save_flags(flags); cli(); - free_page((unsigned long)tmp_buf); tty_unregister_driver(riscom_driver); put_tty_driver(riscom_driver); restore_flags(flags); diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index b4ea1266b6..f4809c8183 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -118,17 +118,6 @@ struct cyclades_port cy_port[] = { }; #define NR_PORTS ARRAY_SIZE(cy_port) -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static unsigned char *tmp_buf = 0; - /* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra @@ -1132,7 +1121,7 @@ cy_put_char(struct tty_struct *tty, unsigned char ch) if (serial_paranoia_check(info, tty->name, "cy_put_char")) return; - if (!tty || !info->xmit_buf) + if (!info->xmit_buf) return; local_irq_save(flags); @@ -1198,7 +1187,7 @@ cy_write(struct tty_struct * tty, return 0; } - if (!tty || !info->xmit_buf || !tmp_buf){ + if (!info->xmit_buf){ return 0; } @@ -1983,13 +1972,6 @@ cy_open(struct tty_struct *tty, struct file * filp) tty->driver_data = info; info->tty = tty; - if (!tmp_buf) { - tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL); - if (!tmp_buf){ - return -ENOMEM; - } - } - /* * Start up serial port */ diff --git a/drivers/clocksource/scx200_hrt.c b/drivers/clocksource/scx200_hrt.c index d418b82972..22915cc46b 100644 --- a/drivers/clocksource/scx200_hrt.c +++ b/drivers/clocksource/scx200_hrt.c @@ -63,7 +63,7 @@ static struct clocksource cs_hrt = { static int __init init_hrt_clocksource(void) { - /* Make sure scx200 has initializedd the configuration block */ + /* Make sure scx200 has initialized the configuration block */ if (!scx200_cb_present()) return -ENODEV; @@ -76,7 +76,7 @@ static int __init init_hrt_clocksource(void) } /* write timer config */ - outb(HR_TMEN | (mhz27) ? HR_TMCLKSEL : 0, + outb(HR_TMEN | (mhz27 ? HR_TMCLKSEL : 0), scx200_cb_base + SCx200_TMCNFG_OFFSET); if (mhz27) { diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 2caaf71d80..86e69b7f91 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -52,8 +52,14 @@ static void handle_update(void *data); * The mutex locks both lists. */ static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list); -static BLOCKING_NOTIFIER_HEAD(cpufreq_transition_notifier_list); +static struct srcu_notifier_head cpufreq_transition_notifier_list; +static int __init init_cpufreq_transition_notifier_list(void) +{ + srcu_init_notifier_head(&cpufreq_transition_notifier_list); + return 0; +} +core_initcall(init_cpufreq_transition_notifier_list); static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX (cpufreq_governor_mutex); @@ -262,14 +268,14 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) freqs->old = policy->cur; } } - blocking_notifier_call_chain(&cpufreq_transition_notifier_list, + srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); adjust_jiffies(CPUFREQ_PRECHANGE, freqs); break; case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); - blocking_notifier_call_chain(&cpufreq_transition_notifier_list, + srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); if (likely(policy) && likely(policy->cpu == freqs->cpu)) policy->cur = freqs->new; @@ -1049,7 +1055,7 @@ static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg) freqs.old = cpu_policy->cur; freqs.new = cur_freq; - blocking_notifier_call_chain(&cpufreq_transition_notifier_list, + srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_SUSPENDCHANGE, &freqs); adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); @@ -1130,7 +1136,7 @@ static int cpufreq_resume(struct sys_device * sysdev) freqs.old = cpu_policy->cur; freqs.new = cur_freq; - blocking_notifier_call_chain( + srcu_notifier_call_chain( &cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs); adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); @@ -1176,7 +1182,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: - ret = blocking_notifier_chain_register( + ret = srcu_notifier_chain_register( &cpufreq_transition_notifier_list, nb); break; case CPUFREQ_POLICY_NOTIFIER: @@ -1208,7 +1214,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: - ret = blocking_notifier_chain_unregister( + ret = srcu_notifier_chain_unregister( &cpufreq_transition_notifier_list, nb); break; case CPUFREQ_POLICY_NOTIFIER: diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index 489022bdef..0945336c28 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -13,7 +13,6 @@ * */ - #include #include "hisax.h" #include "isac.h" @@ -45,33 +44,31 @@ static const char *niccy_revision = "$Revision: 1.21.2.4 $"; #define PCI_IRQ_DISABLE 0xff0000 #define PCI_IRQ_ASSERT 0x800000 -static inline u_char -readreg(unsigned int ale, unsigned int adr, u_char off) +static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) { register u_char ret; byteout(ale, off); ret = bytein(adr); - return (ret); + return ret; } -static inline void -readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +static inline void readfifo(unsigned int ale, unsigned int adr, u_char off, + u_char *data, int size) { byteout(ale, off); insb(adr, data, size); } - -static inline void -writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +static inline void writereg(unsigned int ale, unsigned int adr, u_char off, + u_char data) { byteout(ale, off); byteout(adr, data); } -static inline void -writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +static inline void writefifo(unsigned int ale, unsigned int adr, u_char off, + u_char *data, int size) { byteout(ale, off); outsb(adr, data, size); @@ -79,39 +76,34 @@ writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int siz /* Interface functions */ -static u_char -ReadISAC(struct IsdnCardState *cs, u_char offset) +static u_char ReadISAC(struct IsdnCardState *cs, u_char offset) { - return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset)); + return readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset); } -static void -WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +static void WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); } -static void -ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +static void ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); } -static void -WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +static void WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); } -static u_char -ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +static u_char ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - return (readreg(cs->hw.niccy.hscx_ale, - cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0))); + return readreg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)); } -static void -WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +static void WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, + u_char value) { writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); @@ -130,8 +122,8 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) #include "hscx_irq.c" -static irqreturn_t -niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) +static irqreturn_t niccy_interrupt(int intno, void *dev_id, + struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char val; @@ -141,21 +133,23 @@ niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) if (cs->subtyp == NICCY_PCI) { int ival; ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); - if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */ + if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */ spin_unlock_irqrestore(&cs->lock, flags); return IRQ_NONE; } outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); } - val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); - Start_HSCX: + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, + HSCX_ISTA + 0x40); +Start_HSCX: if (val) hscx_int_main(cs, val); val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); - Start_ISAC: +Start_ISAC: if (val) isac_interrupt(cs, val); - val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, + HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) debugl1(cs, "HSCX IntStat after IntRoutine"); @@ -168,21 +162,21 @@ niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) goto Start_ISAC; } writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, + 0xFF); writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40,0); spin_unlock_irqrestore(&cs->lock, flags); return IRQ_HANDLED; } -static void -release_io_niccy(struct IsdnCardState *cs) +static void release_io_niccy(struct IsdnCardState *cs) { if (cs->subtyp == NICCY_PCI) { int val; - + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); val &= PCI_IRQ_DISABLE; outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); @@ -194,8 +188,7 @@ release_io_niccy(struct IsdnCardState *cs) } } -static void -niccy_reset(struct IsdnCardState *cs) +static void niccy_reset(struct IsdnCardState *cs) { if (cs->subtyp == NICCY_PCI) { int val; @@ -207,29 +200,28 @@ niccy_reset(struct IsdnCardState *cs) inithscxisac(cs, 3); } -static int -niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) +static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) { u_long flags; switch (mt) { - case CARD_RESET: - spin_lock_irqsave(&cs->lock, flags); - niccy_reset(cs); - spin_unlock_irqrestore(&cs->lock, flags); - return(0); - case CARD_RELEASE: - release_io_niccy(cs); - return(0); - case CARD_INIT: - spin_lock_irqsave(&cs->lock, flags); - niccy_reset(cs); - spin_unlock_irqrestore(&cs->lock, flags); - return(0); - case CARD_TEST: - return(0); + case CARD_RESET: + spin_lock_irqsave(&cs->lock, flags); + niccy_reset(cs); + spin_unlock_irqrestore(&cs->lock, flags); + return 0; + case CARD_RELEASE: + release_io_niccy(cs); + return 0; + case CARD_INIT: + spin_lock_irqsave(&cs->lock, flags); + niccy_reset(cs); + spin_unlock_irqrestore(&cs->lock, flags); + return 0; + case CARD_TEST: + return 0; } - return(0); + return 0; } static struct pci_dev *niccy_dev __devinitdata = NULL; @@ -237,8 +229,7 @@ static struct pci_dev *niccy_dev __devinitdata = NULL; static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __devinit -setup_niccy(struct IsdnCard *card) +int __devinit setup_niccy(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -246,40 +237,44 @@ setup_niccy(struct IsdnCard *card) strcpy(tmp, niccy_revision); printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_NICCY) - return (0); + return 0; #ifdef __ISAPNP__ if (!card->para[1] && isapnp_present()) { struct pnp_dev *pnp_d = NULL; int err; - if ((pnp_c = pnp_find_card( - ISAPNP_VENDOR('S', 'D', 'A'), - ISAPNP_FUNCTION(0x0150), pnp_c))) { - if (!(pnp_d = pnp_find_dev(pnp_c, - ISAPNP_VENDOR('S', 'D', 'A'), - ISAPNP_FUNCTION(0x0150), pnp_d))) { - printk(KERN_ERR "NiccyPnP: PnP error card found, no device\n"); - return (0); + pnp_c = pnp_find_card(ISAPNP_VENDOR('S', 'D', 'A'), + ISAPNP_FUNCTION(0x0150), pnp_c); + if (pnp_c) { + pnp_d = pnp_find_dev(pnp_c, + ISAPNP_VENDOR('S', 'D', 'A'), + ISAPNP_FUNCTION(0x0150), pnp_d); + if (!pnp_d) { + printk(KERN_ERR "NiccyPnP: PnP error card " + "found, no device\n"); + return 0; } pnp_disable_dev(pnp_d); err = pnp_activate_dev(pnp_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); + if (err < 0) { + printk(KERN_WARNING "%s: pnp_activate_dev " + "ret(%d)\n", __FUNCTION__, err); + return 0; } card->para[1] = pnp_port_start(pnp_d, 0); card->para[2] = pnp_port_start(pnp_d, 1); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1] || !card->para[2]) { - printk(KERN_ERR "NiccyPnP:some resources are missing %ld/%lx/%lx\n", - card->para[0], card->para[1], card->para[2]); + if (!card->para[0] || !card->para[1] || + !card->para[2]) { + printk(KERN_ERR "NiccyPnP:some resources are " + "missing %ld/%lx/%lx\n", + card->para[0], card->para[1], + card->para[2]); pnp_disable_dev(pnp_d); - return(0); + return 0; } - } else { + } else printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n"); - } } #endif if (card->para[1]) { @@ -291,50 +286,51 @@ setup_niccy(struct IsdnCard *card) cs->subtyp = NICCY_PNP; cs->irq = card->para[0]; if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) { - printk(KERN_WARNING - "HiSax: %s data port %x-%x already in use\n", - CardType[card->typ], - cs->hw.niccy.isac, - cs->hw.niccy.isac + 1); - return (0); + printk(KERN_WARNING "HiSax: %s data port %x-%x " + "already in use\n", CardType[card->typ], + cs->hw.niccy.isac, cs->hw.niccy.isac + 1); + return 0; } if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) { - printk(KERN_WARNING - "HiSax: %s address port %x-%x already in use\n", - CardType[card->typ], + printk(KERN_WARNING "HiSax: %s address port %x-%x " + "already in use\n", CardType[card->typ], cs->hw.niccy.isac_ale, cs->hw.niccy.isac_ale + 1); release_region(cs->hw.niccy.isac, 2); - return (0); + return 0; } } else { #ifdef CONFIG_PCI u_int pci_ioaddr; cs->subtyp = 0; if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM, - PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) { + PCI_DEVICE_ID_SATSAGEM_NICCY, + niccy_dev))) { if (pci_enable_device(niccy_dev)) - return(0); + return 0; /* get IRQ */ if (!niccy_dev->irq) { - printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); - return(0); + printk(KERN_WARNING + "Niccy: No IRQ for PCI card found\n"); + return 0; } cs->irq = niccy_dev->irq; cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0); if (!cs->hw.niccy.cfg_reg) { - printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); - return(0); + printk(KERN_WARNING + "Niccy: No IO-Adr for PCI cfg found\n"); + return 0; } pci_ioaddr = pci_resource_start(niccy_dev, 1); if (!pci_ioaddr) { - printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); - return(0); + printk(KERN_WARNING + "Niccy: No IO-Adr for PCI card found\n"); + return 0; } cs->subtyp = NICCY_PCI; } else { printk(KERN_WARNING "Niccy: No PCI card found\n"); - return(0); + return 0; } cs->irq_flags |= IRQF_SHARED; cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; @@ -343,29 +339,28 @@ setup_niccy(struct IsdnCard *card) cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; if (!request_region(cs->hw.niccy.isac, 4, "niccy")) { printk(KERN_WARNING - "HiSax: %s data port %x-%x already in use\n", - CardType[card->typ], - cs->hw.niccy.isac, - cs->hw.niccy.isac + 4); - return (0); + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, cs->hw.niccy.isac + 4); + return 0; } if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) { printk(KERN_WARNING "HiSax: %s pci port %x-%x already in use\n", - CardType[card->typ], - cs->hw.niccy.cfg_reg, - cs->hw.niccy.cfg_reg + 0x40); + CardType[card->typ], + cs->hw.niccy.cfg_reg, + cs->hw.niccy.cfg_reg + 0x40); release_region(cs->hw.niccy.isac, 4); - return (0); + return 0; } #else printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); - return (0); -#endif /* CONFIG_PCI */ + return 0; +#endif /* CONFIG_PCI */ } printk(KERN_INFO "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n", - CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI", + CardType[cs->typ], (cs->subtyp == 1) ? "PnP" : "PCI", cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); setup_isac(cs); cs->readisac = &ReadISAC; @@ -379,10 +374,10 @@ setup_niccy(struct IsdnCard *card) cs->irq_func = &niccy_interrupt; ISACVersion(cs, "Niccy:"); if (HscxVersion(cs, "Niccy:")) { - printk(KERN_WARNING - "Niccy: wrong HSCX versions check IO address\n"); + printk(KERN_WARNING "Niccy: wrong HSCX versions check IO " + "address\n"); release_io_niccy(cs); - return (0); + return 0; } - return (1); + return 1; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7fc692a8f5..3df0e7a07c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -18,7 +18,7 @@ config IBM_ASM service processor board as a regular serial port. To make use of this feature serial driver support (CONFIG_SERIAL_8250) must be enabled. - + WARNING: This software may not be supported or function correctly on your IBM server. Please consult the IBM ServerProven website for @@ -28,5 +28,33 @@ config IBM_ASM If unsure, say N. -endmenu +config TIFM_CORE + tristate "TI Flash Media interface support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you want support for Texas Instruments(R) Flash Media adapters + you should select this option and then also choose an appropriate + host adapter, such as 'TI Flash Media PCI74xx/PCI76xx host adapter + support', if you have a TI PCI74xx compatible card reader, for + example. + You will also have to select some flash card format drivers. MMC/SD + cards are supported via 'MMC/SD Card support: TI Flash Media MMC/SD + Interface support (MMC_TIFM_SD)'. + + To compile this driver as a module, choose M here: the module will + be called tifm_core. +config TIFM_7XX1 + tristate "TI Flash Media PCI74xx/PCI76xx host adapter support (EXPERIMENTAL)" + depends on PCI && TIFM_CORE && EXPERIMENTAL + default TIFM_CORE + help + This option enables support for Texas Instruments(R) PCI74xx and + PCI76xx families of Flash Media adapters, found in many laptops. + To make actual use of the device, you will have to select some + flash card format drivers, as outlined in the TIFM_CORE Help. + + To compile this driver as a module, choose M here: the module will + be called tifm_7xx1. + +endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c1bf1fb04c..d65ece7609 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -6,3 +6,5 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ obj-$(CONFIG_LKDTM) += lkdtm.o +obj-$(CONFIG_TIFM_CORE) += tifm_core.o +obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c new file mode 100644 index 0000000000..a7ed304461 --- /dev/null +++ b/drivers/misc/tifm_7xx1.c @@ -0,0 +1,437 @@ +/* + * tifm_7xx1.c - TI FlashMedia driver + * + * Copyright (C) 2006 Alex Dubov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#define DRIVER_NAME "tifm_7xx1" +#define DRIVER_VERSION "0.6" + +static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) +{ + int cnt; + unsigned long flags; + + spin_lock_irqsave(&fm->lock, flags); + if (!fm->inhibit_new_cards) { + for (cnt = 0; cnt < fm->max_sockets; cnt++) { + if (fm->sockets[cnt] == sock) { + fm->remove_mask |= (1 << cnt); + queue_work(fm->wq, &fm->media_remover); + break; + } + } + } + spin_unlock_irqrestore(&fm->lock, flags); +} + +static void tifm_7xx1_remove_media(void *adapter) +{ + struct tifm_adapter *fm = adapter; + unsigned long flags; + int cnt; + struct tifm_dev *sock; + + if (!class_device_get(&fm->cdev)) + return; + spin_lock_irqsave(&fm->lock, flags); + for (cnt = 0; cnt < fm->max_sockets; cnt++) { + if (fm->sockets[cnt] && (fm->remove_mask & (1 << cnt))) { + printk(KERN_INFO DRIVER_NAME + ": demand removing card from socket %d\n", cnt); + sock = fm->sockets[cnt]; + fm->sockets[cnt] = 0; + fm->remove_mask &= ~(1 << cnt); + + writel(0x0e00, sock->addr + SOCK_CONTROL); + + writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, + fm->addr + FM_SET_INTERRUPT_ENABLE); + + spin_unlock_irqrestore(&fm->lock, flags); + device_unregister(&sock->dev); + spin_lock_irqsave(&fm->lock, flags); + } + } + spin_unlock_irqrestore(&fm->lock, flags); + class_device_put(&fm->cdev); +} + +static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct tifm_adapter *fm = dev_id; + unsigned int irq_status; + unsigned int sock_irq_status, cnt; + + spin_lock(&fm->lock); + irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); + if (irq_status == 0 || irq_status == (~0)) { + spin_unlock(&fm->lock); + return IRQ_NONE; + } + + if (irq_status & TIFM_IRQ_ENABLE) { + writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + + for (cnt = 0; cnt < fm->max_sockets; cnt++) { + sock_irq_status = (irq_status >> cnt) & + (TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK); + + if (fm->sockets[cnt]) { + if (sock_irq_status && + fm->sockets[cnt]->signal_irq) + sock_irq_status = fm->sockets[cnt]-> + signal_irq(fm->sockets[cnt], + sock_irq_status); + + if (irq_status & (1 << cnt)) + fm->remove_mask |= 1 << cnt; + } else { + if (irq_status & (1 << cnt)) + fm->insert_mask |= 1 << cnt; + } + } + } + writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); + + if (!fm->inhibit_new_cards) { + if (!fm->remove_mask && !fm->insert_mask) { + writel(TIFM_IRQ_ENABLE, + fm->addr + FM_SET_INTERRUPT_ENABLE); + } else { + queue_work(fm->wq, &fm->media_remover); + queue_work(fm->wq, &fm->media_inserter); + } + } + + spin_unlock(&fm->lock); + return IRQ_HANDLED; +} + +static tifm_media_id tifm_7xx1_toggle_sock_power(char *sock_addr, int is_x2) +{ + unsigned int s_state; + int cnt; + + writel(0x0e00, sock_addr + SOCK_CONTROL); + + for (cnt = 0; cnt < 100; cnt++) { + if (!(TIFM_SOCK_STATE_POWERED & + readl(sock_addr + SOCK_PRESENT_STATE))) + break; + msleep(10); + } + + s_state = readl(sock_addr + SOCK_PRESENT_STATE); + if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) + return FM_NULL; + + if (is_x2) { + writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); + } else { + // SmartMedia cards need extra 40 msec + if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1) + msleep(40); + writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, + sock_addr + SOCK_CONTROL); + msleep(10); + writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED, + sock_addr + SOCK_CONTROL); + } + + for (cnt = 0; cnt < 100; cnt++) { + if ((TIFM_SOCK_STATE_POWERED & + readl(sock_addr + SOCK_PRESENT_STATE))) + break; + msleep(10); + } + + if (!is_x2) + writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), + sock_addr + SOCK_CONTROL); + + return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; +} + +inline static char *tifm_7xx1_sock_addr(char *base_addr, unsigned int sock_num) +{ + return base_addr + ((sock_num + 1) << 10); +} + +static void tifm_7xx1_insert_media(void *adapter) +{ + struct tifm_adapter *fm = adapter; + unsigned long flags; + tifm_media_id media_id; + char *card_name = "xx"; + int cnt, ok_to_register; + unsigned int insert_mask; + struct tifm_dev *new_sock = 0; + + if (!class_device_get(&fm->cdev)) + return; + spin_lock_irqsave(&fm->lock, flags); + insert_mask = fm->insert_mask; + fm->insert_mask = 0; + if (fm->inhibit_new_cards) { + spin_unlock_irqrestore(&fm->lock, flags); + class_device_put(&fm->cdev); + return; + } + spin_unlock_irqrestore(&fm->lock, flags); + + for (cnt = 0; cnt < fm->max_sockets; cnt++) { + if (!(insert_mask & (1 << cnt))) + continue; + + media_id = tifm_7xx1_toggle_sock_power(tifm_7xx1_sock_addr(fm->addr, cnt), + fm->max_sockets == 2); + if (media_id) { + ok_to_register = 0; + new_sock = tifm_alloc_device(fm, cnt); + if (new_sock) { + new_sock->addr = tifm_7xx1_sock_addr(fm->addr, + cnt); + new_sock->media_id = media_id; + switch (media_id) { + case 1: + card_name = "xd"; + break; + case 2: + card_name = "ms"; + break; + case 3: + card_name = "sd"; + break; + default: + break; + } + snprintf(new_sock->dev.bus_id, BUS_ID_SIZE, + "tifm_%s%u:%u", card_name, fm->id, cnt); + printk(KERN_INFO DRIVER_NAME + ": %s card detected in socket %d\n", + card_name, cnt); + spin_lock_irqsave(&fm->lock, flags); + if (!fm->sockets[cnt]) { + fm->sockets[cnt] = new_sock; + ok_to_register = 1; + } + spin_unlock_irqrestore(&fm->lock, flags); + if (!ok_to_register || + device_register(&new_sock->dev)) { + spin_lock_irqsave(&fm->lock, flags); + fm->sockets[cnt] = 0; + spin_unlock_irqrestore(&fm->lock, + flags); + tifm_free_device(&new_sock->dev); + } + } + } + writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, + fm->addr + FM_SET_INTERRUPT_ENABLE); + } + + writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); + class_device_put(&fm->cdev); +} + +static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) +{ + struct tifm_adapter *fm = pci_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&fm->lock, flags); + fm->inhibit_new_cards = 1; + fm->remove_mask = 0xf; + fm->insert_mask = 0; + writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&fm->lock, flags); + flush_workqueue(fm->wq); + + tifm_7xx1_remove_media(fm); + + pci_set_power_state(dev, PCI_D3hot); + pci_disable_device(dev); + pci_save_state(dev); + return 0; +} + +static int tifm_7xx1_resume(struct pci_dev *dev) +{ + struct tifm_adapter *fm = pci_get_drvdata(dev); + unsigned long flags; + + pci_restore_state(dev); + pci_enable_device(dev); + pci_set_power_state(dev, PCI_D0); + pci_set_master(dev); + + spin_lock_irqsave(&fm->lock, flags); + fm->inhibit_new_cards = 0; + writel(TIFM_IRQ_SETALL, fm->addr + FM_INTERRUPT_STATUS); + writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, + fm->addr + FM_SET_INTERRUPT_ENABLE); + fm->insert_mask = 0xf; + spin_unlock_irqrestore(&fm->lock, flags); + return 0; +} + +static int tifm_7xx1_probe(struct pci_dev *dev, + const struct pci_device_id *dev_id) +{ + struct tifm_adapter *fm; + int pci_dev_busy = 0; + int rc; + + rc = pci_set_dma_mask(dev, DMA_32BIT_MASK); + if (rc) + return rc; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + pci_set_master(dev); + + rc = pci_request_regions(dev, DRIVER_NAME); + if (rc) { + pci_dev_busy = 1; + goto err_out; + } + + pci_intx(dev, 1); + + fm = tifm_alloc_adapter(); + if (!fm) { + rc = -ENOMEM; + goto err_out_int; + } + + fm->dev = &dev->dev; + fm->max_sockets = (dev->device == 0x803B) ? 2 : 4; + fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->max_sockets, + GFP_KERNEL); + if (!fm->sockets) + goto err_out_free; + + INIT_WORK(&fm->media_inserter, tifm_7xx1_insert_media, fm); + INIT_WORK(&fm->media_remover, tifm_7xx1_remove_media, fm); + fm->eject = tifm_7xx1_eject; + pci_set_drvdata(dev, fm); + + fm->addr = ioremap(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + if (!fm->addr) + goto err_out_free; + + rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm); + if (rc) + goto err_out_unmap; + + rc = tifm_add_adapter(fm); + if (rc) + goto err_out_irq; + + writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, + fm->addr + FM_SET_INTERRUPT_ENABLE); + + fm->insert_mask = 0xf; + + return 0; + +err_out_irq: + free_irq(dev->irq, fm); +err_out_unmap: + iounmap(fm->addr); +err_out_free: + pci_set_drvdata(dev, NULL); + tifm_free_adapter(fm); +err_out_int: + pci_intx(dev, 0); + pci_release_regions(dev); +err_out: + if (!pci_dev_busy) + pci_disable_device(dev); + return rc; +} + +static void tifm_7xx1_remove(struct pci_dev *dev) +{ + struct tifm_adapter *fm = pci_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&fm->lock, flags); + fm->inhibit_new_cards = 1; + fm->remove_mask = 0xf; + fm->insert_mask = 0; + writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&fm->lock, flags); + + flush_workqueue(fm->wq); + + tifm_7xx1_remove_media(fm); + + writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + free_irq(dev->irq, fm); + + tifm_remove_adapter(fm); + + pci_set_drvdata(dev, 0); + + iounmap(fm->addr); + pci_intx(dev, 0); + pci_release_regions(dev); + + pci_disable_device(dev); + tifm_free_adapter(fm); +} + +static struct pci_device_id tifm_7xx1_pci_tbl [] = { + { PCI_VENDOR_ID_TI, 0x8033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + 0 }, /* xx21 - the one I have */ + { PCI_VENDOR_ID_TI, 0x803B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + 0 }, /* xx12 - should be also supported */ + { } +}; + +static struct pci_driver tifm_7xx1_driver = { + .name = DRIVER_NAME, + .id_table = tifm_7xx1_pci_tbl, + .probe = tifm_7xx1_probe, + .remove = tifm_7xx1_remove, + .suspend = tifm_7xx1_suspend, + .resume = tifm_7xx1_resume, +}; + +static int __init tifm_7xx1_init(void) +{ + return pci_register_driver(&tifm_7xx1_driver); +} + +static void __exit tifm_7xx1_exit(void) +{ + pci_unregister_driver(&tifm_7xx1_driver); +} + +MODULE_AUTHOR("Alex Dubov"); +MODULE_DESCRIPTION("TI FlashMedia host driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl); +MODULE_VERSION(DRIVER_VERSION); + +module_init(tifm_7xx1_init); +module_exit(tifm_7xx1_exit); diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c new file mode 100644 index 0000000000..cca5f85224 --- /dev/null +++ b/drivers/misc/tifm_core.c @@ -0,0 +1,272 @@ +/* + * tifm_core.c - TI FlashMedia driver + * + * Copyright (C) 2006 Alex Dubov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#define DRIVER_NAME "tifm_core" +#define DRIVER_VERSION "0.6" + +static DEFINE_IDR(tifm_adapter_idr); +static DEFINE_SPINLOCK(tifm_adapter_lock); + +static tifm_media_id *tifm_device_match(tifm_media_id *ids, + struct tifm_dev *dev) +{ + while (*ids) { + if (dev->media_id == *ids) + return ids; + ids++; + } + return NULL; +} + +static int tifm_match(struct device *dev, struct device_driver *drv) +{ + struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *fm_drv; + + fm_drv = container_of(drv, struct tifm_driver, driver); + if (!fm_drv->id_table) + return -EINVAL; + if (tifm_device_match(fm_drv->id_table, fm_dev)) + return 1; + return -ENODEV; +} + +static int tifm_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct tifm_dev *fm_dev; + int i = 0; + int length = 0; + const char *card_type_name[] = {"INV", "SM", "MS", "SD"}; + + if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev))) + return -ENODEV; + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, + "TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id])) + return -ENOMEM; + + return 0; +} + +static struct bus_type tifm_bus_type = { + .name = "tifm", + .match = tifm_match, + .uevent = tifm_uevent, +}; + +static void tifm_free(struct class_device *cdev) +{ + struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); + + kfree(fm->sockets); + if (fm->wq) + destroy_workqueue(fm->wq); + kfree(fm); +} + +static struct class tifm_adapter_class = { + .name = "tifm_adapter", + .release = tifm_free +}; + +struct tifm_adapter *tifm_alloc_adapter(void) +{ + struct tifm_adapter *fm; + + fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL); + if (fm) { + fm->cdev.class = &tifm_adapter_class; + spin_lock_init(&fm->lock); + class_device_initialize(&fm->cdev); + } + return fm; +} +EXPORT_SYMBOL(tifm_alloc_adapter); + +void tifm_free_adapter(struct tifm_adapter *fm) +{ + class_device_put(&fm->cdev); +} +EXPORT_SYMBOL(tifm_free_adapter); + +int tifm_add_adapter(struct tifm_adapter *fm) +{ + int rc; + + if (!idr_pre_get(&tifm_adapter_idr, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&tifm_adapter_lock); + rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); + spin_unlock(&tifm_adapter_lock); + if (!rc) { + snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); + strncpy(fm->wq_name, fm->cdev.class_id, KOBJ_NAME_LEN); + + fm->wq = create_singlethread_workqueue(fm->wq_name); + if (fm->wq) + return class_device_add(&fm->cdev); + + spin_lock(&tifm_adapter_lock); + idr_remove(&tifm_adapter_idr, fm->id); + spin_unlock(&tifm_adapter_lock); + rc = -ENOMEM; + } + return rc; +} +EXPORT_SYMBOL(tifm_add_adapter); + +void tifm_remove_adapter(struct tifm_adapter *fm) +{ + class_device_del(&fm->cdev); + + spin_lock(&tifm_adapter_lock); + idr_remove(&tifm_adapter_idr, fm->id); + spin_unlock(&tifm_adapter_lock); +} +EXPORT_SYMBOL(tifm_remove_adapter); + +void tifm_free_device(struct device *dev) +{ + struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + if (fm_dev->wq) + destroy_workqueue(fm_dev->wq); + kfree(fm_dev); +} +EXPORT_SYMBOL(tifm_free_device); + +struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id) +{ + struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); + + if (dev) { + spin_lock_init(&dev->lock); + snprintf(dev->wq_name, KOBJ_NAME_LEN, "tifm%u:%u", fm->id, id); + dev->wq = create_singlethread_workqueue(dev->wq_name); + if (!dev->wq) { + kfree(dev); + return 0; + } + dev->dev.parent = fm->dev; + dev->dev.bus = &tifm_bus_type; + dev->dev.release = tifm_free_device; + } + return dev; +} +EXPORT_SYMBOL(tifm_alloc_device); + +void tifm_eject(struct tifm_dev *sock) +{ + struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent); + fm->eject(fm, sock); +} +EXPORT_SYMBOL(tifm_eject); + +int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, + int direction) +{ + return pci_map_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); +} +EXPORT_SYMBOL(tifm_map_sg); + +void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, + int direction) +{ + pci_unmap_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); +} +EXPORT_SYMBOL(tifm_unmap_sg); + +static int tifm_device_probe(struct device *dev) +{ + struct tifm_driver *drv; + struct tifm_dev *fm_dev; + int rc = 0; + const tifm_media_id *id; + + drv = container_of(dev->driver, struct tifm_driver, driver); + fm_dev = container_of(dev, struct tifm_dev, dev); + get_device(dev); + if (!fm_dev->drv && drv->probe && drv->id_table) { + rc = -ENODEV; + id = tifm_device_match(drv->id_table, fm_dev); + if (id) + rc = drv->probe(fm_dev); + if (rc >= 0) { + rc = 0; + fm_dev->drv = drv; + } + } + if (rc) + put_device(dev); + return rc; +} + +static int tifm_device_remove(struct device *dev) +{ + struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *drv = fm_dev->drv; + + if (drv) { + if (drv->remove) drv->remove(fm_dev); + fm_dev->drv = 0; + } + + put_device(dev); + return 0; +} + +int tifm_register_driver(struct tifm_driver *drv) +{ + drv->driver.bus = &tifm_bus_type; + drv->driver.probe = tifm_device_probe; + drv->driver.remove = tifm_device_remove; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(tifm_register_driver); + +void tifm_unregister_driver(struct tifm_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(tifm_unregister_driver); + +static int __init tifm_init(void) +{ + int rc = bus_register(&tifm_bus_type); + + if (!rc) { + rc = class_register(&tifm_adapter_class); + if (rc) + bus_unregister(&tifm_bus_type); + } + + return rc; +} + +static void __exit tifm_exit(void) +{ + class_unregister(&tifm_adapter_class); + bus_unregister(&tifm_bus_type); +} + +subsys_initcall(tifm_init); +module_exit(tifm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alex Dubov"); +MODULE_DESCRIPTION("TI FlashMedia core driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index f540bd88dc..ea41852ec8 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -109,4 +109,20 @@ config MMC_IMX If unsure, say N. +config MMC_TIFM_SD + tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)" + depends on MMC && EXPERIMENTAL + select TIFM_CORE + help + Say Y here if you want to be able to access MMC/SD cards with + the Texas Instruments(R) Flash Media card reader, found in many + laptops. + This option 'selects' (turns on, enables) 'TIFM_CORE', but you + probably also need appropriate card reader host adapter, such as + 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support + (TIFM_7XX1)'. + + To compile this driver as a module, choose M here: the + module will be called tifm_sd. + endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index b1f6e03e7a..acfd4de0ab 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o +obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o mmc_core-y := mmc.o mmc_sysfs.o mmc_core-$(CONFIG_BLOCK) += mmc_queue.o diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5b9caa7978..ee8863c123 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1166,9 +1166,9 @@ static void mmc_setup(struct mmc_host *host) void mmc_detect_change(struct mmc_host *host, unsigned long delay) { if (delay) - schedule_delayed_work(&host->detect, delay); + mmc_schedule_delayed_work(&host->detect, delay); else - schedule_work(&host->detect); + mmc_schedule_work(&host->detect); } EXPORT_SYMBOL(mmc_detect_change); @@ -1311,7 +1311,7 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { - flush_scheduled_work(); + mmc_flush_scheduled_work(); mmc_free_host_sysfs(host); } diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h index 97bae00292..cd5e0ab3d8 100644 --- a/drivers/mmc/mmc.h +++ b/drivers/mmc/mmc.h @@ -18,4 +18,8 @@ struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); int mmc_add_host_sysfs(struct mmc_host *host); void mmc_remove_host_sysfs(struct mmc_host *host); void mmc_free_host_sysfs(struct mmc_host *host); + +int mmc_schedule_work(struct work_struct *work); +int mmc_schedule_delayed_work(struct work_struct *work, unsigned long delay); +void mmc_flush_scheduled_work(void); #endif diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index db0e8ad439..c1293f1bda 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -158,13 +158,13 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; + struct mmc_blk_request brq; int ret; if (mmc_card_claim_host(card)) goto cmd_err; do { - struct mmc_blk_request brq; struct mmc_command cmd; u32 readcmd, writecmd; @@ -278,17 +278,27 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) cmd_err: mmc_card_release_host(card); + ret = 1; + /* - * This is a little draconian, but until we get proper - * error handling sorted out here, its the best we can - * do - especially as some hosts have no idea how much - * data was transferred before the error occurred. + * For writes and where the host claims to support proper + * error reporting, we first ok the successful blocks. + * + * For reads we just fail the entire chunk as that should + * be safe in all cases. */ + if (rq_data_dir(req) != READ && + (card->host->caps & MMC_CAP_MULTIWRITE)) { + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + spin_unlock_irq(&md->lock); + } + spin_lock_irq(&md->lock); - do { + while (ret) { ret = end_that_request_chunk(req, 0, req->current_nr_sectors << 9); - } while (ret); + } add_disk_randomness(req->rq_disk); blkdev_dequeue_request(req); diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index a2a35fd946..10cc9734ea 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -317,10 +318,41 @@ void mmc_free_host_sysfs(struct mmc_host *host) class_device_put(&host->class_dev); } +static struct workqueue_struct *workqueue; + +/* + * Internal function. Schedule work in the MMC work queue. + */ +int mmc_schedule_work(struct work_struct *work) +{ + return queue_work(workqueue, work); +} + +/* + * Internal function. Schedule delayed work in the MMC work queue. + */ +int mmc_schedule_delayed_work(struct work_struct *work, unsigned long delay) +{ + return queue_delayed_work(workqueue, work, delay); +} + +/* + * Internal function. Flush all scheduled work from the MMC work queue. + */ +void mmc_flush_scheduled_work(void) +{ + flush_workqueue(workqueue); +} static int __init mmc_init(void) { - int ret = bus_register(&mmc_bus_type); + int ret; + + workqueue = create_singlethread_workqueue("kmmcd"); + if (!workqueue) + return -ENOMEM; + + ret = bus_register(&mmc_bus_type); if (ret == 0) { ret = class_register(&mmc_host_class); if (ret) @@ -333,6 +365,7 @@ static void __exit mmc_exit(void) { class_unregister(&mmc_host_class); bus_unregister(&mmc_bus_type); + destroy_workqueue(workqueue); } module_init(mmc_init); diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4dab5ec392..20711acb01 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -35,6 +35,8 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) #define SDHCI_QUIRK_FORCE_DMA (1<<1) +/* Controller doesn't like some resets when there is no card inserted. */ +#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -51,7 +53,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .device = PCI_DEVICE_ID_RICOH_R5C822, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_FORCE_DMA, + .driver_data = SDHCI_QUIRK_FORCE_DMA | + SDHCI_QUIRK_NO_CARD_NO_RESET, }, { @@ -125,6 +128,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; + if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT)) + return; + } + writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); if (mask & SDHCI_RESET_ALL) @@ -717,6 +726,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) } else sdhci_send_command(host, mrq->cmd); + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -753,6 +763,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl &= ~SDHCI_CTRL_4BITBUS; writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -860,6 +871,7 @@ static void sdhci_tasklet_finish(unsigned long param) sdhci_deactivate_led(host); + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); @@ -893,6 +905,7 @@ static void sdhci_timeout_timer(unsigned long data) } } + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -1030,6 +1043,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id, struct pt_regs *regs) result = IRQ_HANDLED; + mmiowb(); out: spin_unlock(&host->lock); @@ -1095,6 +1109,7 @@ static int sdhci_resume (struct pci_dev *pdev) if (chip->hosts[i]->flags & SDHCI_USE_DMA) pci_set_master(pdev); sdhci_init(chip->hosts[i]); + mmiowb(); ret = mmc_resume_host(chip->hosts[i]->mmc); if (ret) return ret; @@ -1168,6 +1183,9 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) host = mmc_priv(mmc); host->mmc = mmc; + host->chip = chip; + chip->hosts[slot] = host; + host->bar = first_bar + slot; host->addr = pci_resource_start(pdev, host->bar); @@ -1324,8 +1342,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) sdhci_dumpregs(host); #endif - host->chip = chip; - chip->hosts[slot] = host; + mmiowb(); mmc_add_host(mmc); diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c new file mode 100644 index 0000000000..6d23dc08d1 --- /dev/null +++ b/drivers/mmc/tifm_sd.c @@ -0,0 +1,933 @@ +/* + * tifm_sd.c - TI FlashMedia driver + * + * Copyright (C) 2006 Alex Dubov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + + +#include +#include +#include +#include + +#define DRIVER_NAME "tifm_sd" +#define DRIVER_VERSION "0.6" + +static int no_dma = 0; +static int fixed_timeout = 0; +module_param(no_dma, bool, 0644); +module_param(fixed_timeout, bool, 0644); + +/* Constants here are mostly from OMAP5912 datasheet */ +#define TIFM_MMCSD_RESET 0x0002 +#define TIFM_MMCSD_CLKMASK 0x03ff +#define TIFM_MMCSD_POWER 0x0800 +#define TIFM_MMCSD_4BBUS 0x8000 +#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */ +#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */ +#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */ +#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */ +#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ +#define TIFM_MMCSD_READ 0x8000 + +#define TIFM_MMCSD_DATAMASK 0x001d /* set bits: EOFB, BRS, CB, EOC */ +#define TIFM_MMCSD_ERRMASK 0x41e0 /* set bits: CERR, CCRC, CTO, DCRC, DTO */ +#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ +#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ +#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ +#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ +#define TIFM_MMCSD_DTO 0x0020 /* data time-out */ +#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */ +#define TIFM_MMCSD_CTO 0x0080 /* command time-out */ +#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ +#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ +#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ +#define TIFM_MMCSD_CERR 0x4000 /* card status error */ + +#define TIFM_MMCSD_FIFO_SIZE 0x0020 + +#define TIFM_MMCSD_RSP_R0 0x0000 +#define TIFM_MMCSD_RSP_R1 0x0100 +#define TIFM_MMCSD_RSP_R2 0x0200 +#define TIFM_MMCSD_RSP_R3 0x0300 +#define TIFM_MMCSD_RSP_R4 0x0400 +#define TIFM_MMCSD_RSP_R5 0x0500 +#define TIFM_MMCSD_RSP_R6 0x0600 + +#define TIFM_MMCSD_RSP_BUSY 0x0800 + +#define TIFM_MMCSD_CMD_BC 0x0000 +#define TIFM_MMCSD_CMD_BCR 0x1000 +#define TIFM_MMCSD_CMD_AC 0x2000 +#define TIFM_MMCSD_CMD_ADTC 0x3000 + +typedef enum { + IDLE = 0, + CMD, /* main command ended */ + BRS, /* block transfer finished */ + SCMD, /* stop command ended */ + CARD, /* card left busy state */ + FIFO, /* FIFO operation completed (uncertain) */ + READY +} card_state_t; + +enum { + FIFO_RDY = 0x0001, /* hardware dependent value */ + HOST_REG = 0x0002, + EJECT = 0x0004, + EJECT_DONE = 0x0008, + CARD_BUSY = 0x0010, + OPENDRAIN = 0x0040, /* hardware dependent value */ + CARD_EVENT = 0x0100, /* hardware dependent value */ + CARD_RO = 0x0200, /* hardware dependent value */ + FIFO_EVENT = 0x10000 }; /* hardware dependent value */ + +struct tifm_sd { + struct tifm_dev *dev; + + unsigned int flags; + card_state_t state; + unsigned int clk_freq; + unsigned int clk_div; + unsigned long timeout_jiffies; // software timeout - 2 sec + + struct mmc_request *req; + struct work_struct cmd_handler; + struct work_struct abort_handler; + wait_queue_head_t can_eject; + + size_t written_blocks; + char *buffer; + size_t buffer_size; + size_t buffer_pos; + +}; + +static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, + unsigned int host_status) +{ + struct mmc_command *cmd = host->req->cmd; + unsigned int t_val = 0, cnt = 0; + + if (host_status & TIFM_MMCSD_BRS) { + /* in non-dma rx mode BRS fires when fifo is still not empty */ + if (host->buffer && (cmd->data->flags & MMC_DATA_READ)) { + while (host->buffer_size > host->buffer_pos) { + t_val = readl(sock->addr + SOCK_MMCSD_DATA); + host->buffer[host->buffer_pos++] = t_val & 0xff; + host->buffer[host->buffer_pos++] = + (t_val >> 8) & 0xff; + } + } + return 1; + } else if (host->buffer) { + if ((cmd->data->flags & MMC_DATA_READ) && + (host_status & TIFM_MMCSD_AF)) { + for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { + t_val = readl(sock->addr + SOCK_MMCSD_DATA); + if (host->buffer_size > host->buffer_pos) { + host->buffer[host->buffer_pos++] = + t_val & 0xff; + host->buffer[host->buffer_pos++] = + (t_val >> 8) & 0xff; + } + } + } else if ((cmd->data->flags & MMC_DATA_WRITE) + && (host_status & TIFM_MMCSD_AE)) { + for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { + if (host->buffer_size > host->buffer_pos) { + t_val = host->buffer[host->buffer_pos++] & 0x00ff; + t_val |= ((host->buffer[host->buffer_pos++]) << 8) + & 0xff00; + writel(t_val, + sock->addr + SOCK_MMCSD_DATA); + } + } + } + } + return 0; +} + +static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) +{ + unsigned int rc = 0; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + rc |= TIFM_MMCSD_RSP_R0; + break; + case MMC_RSP_R1B: + rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through + case MMC_RSP_R1: + rc |= TIFM_MMCSD_RSP_R1; + break; + case MMC_RSP_R2: + rc |= TIFM_MMCSD_RSP_R2; + break; + case MMC_RSP_R3: + rc |= TIFM_MMCSD_RSP_R3; + break; + case MMC_RSP_R6: + rc |= TIFM_MMCSD_RSP_R6; + break; + default: + BUG(); + } + + switch (mmc_cmd_type(cmd)) { + case MMC_CMD_BC: + rc |= TIFM_MMCSD_CMD_BC; + break; + case MMC_CMD_BCR: + rc |= TIFM_MMCSD_CMD_BCR; + break; + case MMC_CMD_AC: + rc |= TIFM_MMCSD_CMD_AC; + break; + case MMC_CMD_ADTC: + rc |= TIFM_MMCSD_CMD_ADTC; + break; + default: + BUG(); + } + return rc; +} + +static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) +{ + struct tifm_dev *sock = host->dev; + unsigned int cmd_mask = tifm_sd_op_flags(cmd) | + (host->flags & OPENDRAIN); + + if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) + cmd_mask |= TIFM_MMCSD_READ; + + dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", + cmd->opcode, cmd->arg, cmd_mask); + + writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); + writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); + writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); +} + +static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) +{ + cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); + cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); + cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); + cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); +} + +static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, + unsigned int host_status) +{ + struct mmc_command *cmd = host->req->cmd; + +change_state: + switch (host->state) { + case IDLE: + return; + case CMD: + if (host_status & TIFM_MMCSD_EOC) { + tifm_sd_fetch_resp(cmd, sock); + if (cmd->data) { + host->state = BRS; + } else + host->state = READY; + goto change_state; + } + break; + case BRS: + if (tifm_sd_transfer_data(sock, host, host_status)) { + if (!host->req->stop) { + if (cmd->data->flags & MMC_DATA_WRITE) { + host->state = CARD; + } else { + host->state = + host->buffer ? READY : FIFO; + } + goto change_state; + } + tifm_sd_exec(host, host->req->stop); + host->state = SCMD; + } + break; + case SCMD: + if (host_status & TIFM_MMCSD_EOC) { + tifm_sd_fetch_resp(host->req->stop, sock); + if (cmd->error) { + host->state = READY; + } else if (cmd->data->flags & MMC_DATA_WRITE) { + host->state = CARD; + } else { + host->state = host->buffer ? READY : FIFO; + } + goto change_state; + } + break; + case CARD: + if (!(host->flags & CARD_BUSY) + && (host->written_blocks == cmd->data->blocks)) { + host->state = host->buffer ? READY : FIFO; + goto change_state; + } + break; + case FIFO: + if (host->flags & FIFO_RDY) { + host->state = READY; + host->flags &= ~FIFO_RDY; + goto change_state; + } + break; + case READY: + queue_work(sock->wq, &host->cmd_handler); + return; + } + + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); +} + +/* Called from interrupt handler */ +static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, + unsigned int sock_irq_status) +{ + struct tifm_sd *host; + unsigned int host_status = 0, fifo_status = 0; + int error_code = 0; + + spin_lock(&sock->lock); + host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); + cancel_delayed_work(&host->abort_handler); + + if (sock_irq_status & FIFO_EVENT) { + fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); + writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); + + host->flags |= fifo_status & FIFO_RDY; + } + + if (sock_irq_status & CARD_EVENT) { + host_status = readl(sock->addr + SOCK_MMCSD_STATUS); + writel(host_status, sock->addr + SOCK_MMCSD_STATUS); + + if (!(host->flags & HOST_REG)) + queue_work(sock->wq, &host->cmd_handler); + if (!host->req) + goto done; + + if (host_status & TIFM_MMCSD_ERRMASK) { + if (host_status & TIFM_MMCSD_CERR) + error_code = MMC_ERR_FAILED; + else if (host_status & + (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) + error_code = MMC_ERR_TIMEOUT; + else if (host_status & + (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) + error_code = MMC_ERR_BADCRC; + + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); + + if (host->req->stop) { + if (host->state == SCMD) { + host->req->stop->error = error_code; + } else if(host->state == BRS) { + host->req->cmd->error = error_code; + tifm_sd_exec(host, host->req->stop); + queue_delayed_work(sock->wq, + &host->abort_handler, + host->timeout_jiffies); + host->state = SCMD; + goto done; + } else { + host->req->cmd->error = error_code; + } + } else { + host->req->cmd->error = error_code; + } + host->state = READY; + } + + if (host_status & TIFM_MMCSD_CB) + host->flags |= CARD_BUSY; + if ((host_status & TIFM_MMCSD_EOFB) && + (host->flags & CARD_BUSY)) { + host->written_blocks++; + host->flags &= ~CARD_BUSY; + } + } + + if (host->req) + tifm_sd_process_cmd(sock, host, host_status); +done: + dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", + host_status, fifo_status); + spin_unlock(&sock->lock); + return sock_irq_status; +} + +static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) +{ + struct tifm_dev *sock = card->dev; + unsigned int dest_cnt; + + /* DMA style IO */ + + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(long_log2(cmd->data->blksz) - 2, + sock->addr + SOCK_FIFO_PAGE_SIZE); + writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); + writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + + dest_cnt = (cmd->data->blocks) << 8; + + writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS); + + writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(cmd->data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); + + if (cmd->data->flags & MMC_DATA_WRITE) { + writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN, + sock->addr + SOCK_DMA_CONTROL); + } else { + writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); + } +} + +static void tifm_sd_set_data_timeout(struct tifm_sd *host, + struct mmc_data *data) +{ + struct tifm_dev *sock = host->dev; + unsigned int data_timeout = data->timeout_clks; + + if (fixed_timeout) + return; + + data_timeout += data->timeout_ns / + ((1000000000 / host->clk_freq) * host->clk_div); + data_timeout *= 10; // call it fudge factor for now + + if (data_timeout < 0xffff) { + writel((~TIFM_MMCSD_DPE) & + readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); + writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + } else { + writel(TIFM_MMCSD_DPE | + readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); + data_timeout = (data_timeout >> 10) + 1; + if(data_timeout > 0xffff) + data_timeout = 0; /* set to unlimited */ + writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + } +} + +static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + int sg_count = 0; + struct mmc_data *r_data = mrq->cmd->data; + + spin_lock_irqsave(&sock->lock, flags); + if (host->flags & EJECT) { + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (host->req) { + printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (r_data) { + tifm_sd_set_data_timeout(host, r_data); + + sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, + mrq->cmd->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + if (sg_count != 1) { + printk(KERN_ERR DRIVER_NAME + ": scatterlist map failed\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + host->written_blocks = 0; + host->flags &= ~CARD_BUSY; + tifm_sd_prepare_data(host, mrq->cmd); + } + + host->req = mrq; + host->state = CMD; + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); + writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + tifm_sd_exec(host, mrq->cmd); + spin_unlock_irqrestore(&sock->lock, flags); + return; + +err_out: + if (sg_count > 0) + tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + mrq->cmd->error = MMC_ERR_TIMEOUT; + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_end_cmd(void *data) +{ + struct tifm_sd *host = data; + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct mmc_request *mrq; + struct mmc_data *r_data = 0; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + mrq = host->req; + host->req = 0; + host->state = IDLE; + + if (!mrq) { + printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); + spin_unlock_irqrestore(&sock->lock, flags); + return; + } + + r_data = mrq->cmd->data; + if (r_data) { + if (r_data->flags & MMC_DATA_WRITE) { + r_data->bytes_xfered = host->written_blocks * + r_data->blksz; + } else { + r_data->bytes_xfered = r_data->blocks - + readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz - + readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; + } + tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + } + + writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + + spin_unlock_irqrestore(&sock->lock, flags); + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + struct mmc_data *r_data = mrq->cmd->data; + char *t_buffer = 0; + + if (r_data) { + t_buffer = kmap(r_data->sg->page); + if (!t_buffer) { + printk(KERN_ERR DRIVER_NAME ": kmap failed\n"); + goto err_out; + } + } + + spin_lock_irqsave(&sock->lock, flags); + if (host->flags & EJECT) { + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (host->req) { + printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (r_data) { + tifm_sd_set_data_timeout(host, r_data); + + host->buffer = t_buffer + r_data->sg->offset; + host->buffer_size = mrq->cmd->data->blocks * + mrq->cmd->data->blksz; + + writel(TIFM_MMCSD_BUFINT | + readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | + (TIFM_MMCSD_FIFO_SIZE - 1), + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + host->written_blocks = 0; + host->flags &= ~CARD_BUSY; + host->buffer_pos = 0; + writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); + } + + host->req = mrq; + host->state = CMD; + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); + writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + tifm_sd_exec(host, mrq->cmd); + spin_unlock_irqrestore(&sock->lock, flags); + return; + +err_out: + if (t_buffer) + kunmap(r_data->sg->page); + + mrq->cmd->error = MMC_ERR_TIMEOUT; + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_end_cmd_nodma(void *data) +{ + struct tifm_sd *host = (struct tifm_sd*)data; + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct mmc_request *mrq; + struct mmc_data *r_data = 0; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + mrq = host->req; + host->req = 0; + host->state = IDLE; + + if (!mrq) { + printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); + spin_unlock_irqrestore(&sock->lock, flags); + return; + } + + r_data = mrq->cmd->data; + if (r_data) { + writel((~TIFM_MMCSD_BUFINT) & + readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + + if (r_data->flags & MMC_DATA_WRITE) { + r_data->bytes_xfered = host->written_blocks * + r_data->blksz; + } else { + r_data->bytes_xfered = r_data->blocks - + readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz - + readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; + } + host->buffer = 0; + host->buffer_pos = 0; + host->buffer_size = 0; + } + + writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + + spin_unlock_irqrestore(&sock->lock, flags); + + if (r_data) + kunmap(r_data->sg->page); + + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_abort(void *data) +{ + printk(KERN_ERR DRIVER_NAME + ": card failed to respond for a long period of time"); + tifm_eject(((struct tifm_sd*)data)->dev); +} + +static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned int clk_div1, clk_div2; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + dev_dbg(&sock->dev, "Setting bus width %d, power %d\n", ios->bus_width, + ios->power_mode); + if (ios->bus_width == MMC_BUS_WIDTH_4) { + writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); + } else { + writel((~TIFM_MMCSD_4BBUS) & + readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); + } + + if (ios->clock) { + clk_div1 = 20000000 / ios->clock; + if (!clk_div1) + clk_div1 = 1; + + clk_div2 = 24000000 / ios->clock; + if (!clk_div2) + clk_div2 = 1; + + if ((20000000 / clk_div1) > ios->clock) + clk_div1++; + if ((24000000 / clk_div2) > ios->clock) + clk_div2++; + if ((20000000 / clk_div1) > (24000000 / clk_div2)) { + host->clk_freq = 20000000; + host->clk_div = clk_div1; + writel((~TIFM_CTRL_FAST_CLK) & + readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + } else { + host->clk_freq = 24000000; + host->clk_div = clk_div2; + writel(TIFM_CTRL_FAST_CLK | + readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + } + } else { + host->clk_div = 0; + } + host->clk_div &= TIFM_MMCSD_CLKMASK; + writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & + readl(sock->addr + SOCK_MMCSD_CONFIG)), + sock->addr + SOCK_MMCSD_CONFIG); + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + host->flags |= OPENDRAIN; + else + host->flags &= ~OPENDRAIN; + + /* chip_select : maybe later */ + //vdd + //power is set before probe / after remove + //I believe, power_off when already marked for eject is sufficient to + // allow removal. + if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) { + host->flags |= EJECT_DONE; + wake_up_all(&host->can_eject); + } + + spin_unlock_irqrestore(&sock->lock, flags); +} + +static int tifm_sd_ro(struct mmc_host *mmc) +{ + int rc; + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + host->flags |= (CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)); + rc = (host->flags & CARD_RO) ? 1 : 0; + + spin_unlock_irqrestore(&sock->lock, flags); + return rc; +} + +static struct mmc_host_ops tifm_sd_ops = { + .request = tifm_sd_request, + .set_ios = tifm_sd_ios, + .get_ro = tifm_sd_ro +}; + +static void tifm_sd_register_host(void *data) +{ + struct tifm_sd *host = (struct tifm_sd*)data; + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + host->flags |= HOST_REG; + PREPARE_WORK(&host->cmd_handler, + no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, + data); + spin_unlock_irqrestore(&sock->lock, flags); + dev_dbg(&sock->dev, "adding host\n"); + mmc_add_host(mmc); +} + +static int tifm_sd_probe(struct tifm_dev *sock) +{ + struct mmc_host *mmc; + struct tifm_sd *host; + int rc = -EIO; + + if (!(TIFM_SOCK_STATE_OCCUPIED & + readl(sock->addr + SOCK_PRESENT_STATE))) { + printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n"); + return rc; + } + + mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->dev = sock; + host->clk_div = 61; + init_waitqueue_head(&host->can_eject); + INIT_WORK(&host->cmd_handler, tifm_sd_register_host, host); + INIT_WORK(&host->abort_handler, tifm_sd_abort, host); + + tifm_set_drvdata(sock, mmc); + sock->signal_irq = tifm_sd_signal_irq; + + host->clk_freq = 20000000; + host->timeout_jiffies = msecs_to_jiffies(1000); + + tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request; + mmc->ops = &tifm_sd_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->f_min = 20000000 / 60; + mmc->f_max = 24000000; + mmc->max_hw_segs = 1; + mmc->max_phys_segs = 1; + mmc->max_sectors = 127; + mmc->max_seg_size = mmc->max_sectors << 11; //2k maximum hw block length + + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + + for (rc = 0; rc < 50; rc++) { + /* Wait for reset ack */ + if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { + rc = 0; + break; + } + msleep(10); + } + + if (rc) { + printk(KERN_ERR DRIVER_NAME + ": card not ready - probe failed\n"); + mmc_free_host(mmc); + return -ENODEV; + } + + writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_INT_ENABLE); + + writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); // command timeout 64 clocks for now + writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); + + return 0; +} + +static int tifm_sd_host_is_down(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&sock->lock, flags); + rc = (host->flags & EJECT_DONE); + spin_unlock_irqrestore(&sock->lock, flags); + return rc; +} + +static void tifm_sd_remove(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + host->flags |= EJECT; + if (host->req) + queue_work(sock->wq, &host->cmd_handler); + spin_unlock_irqrestore(&sock->lock, flags); + wait_event_timeout(host->can_eject, tifm_sd_host_is_down(sock), + host->timeout_jiffies); + + if (host->flags & HOST_REG) + mmc_remove_host(mmc); + + /* The meaning of the bit majority in this constant is unknown. */ + writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + + tifm_set_drvdata(sock, 0); + mmc_free_host(mmc); +} + +static tifm_media_id tifm_sd_id_tbl[] = { + FM_SD, 0 +}; + +static struct tifm_driver tifm_sd_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE + }, + .id_table = tifm_sd_id_tbl, + .probe = tifm_sd_probe, + .remove = tifm_sd_remove +}; + +static int __init tifm_sd_init(void) +{ + return tifm_register_driver(&tifm_sd_driver); +} + +static void __exit tifm_sd_exit(void) +{ + tifm_unregister_driver(&tifm_sd_driver); +} + +MODULE_AUTHOR("Alex Dubov"); +MODULE_DESCRIPTION("TI FlashMedia SD driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); +MODULE_VERSION(DRIVER_VERSION); + +module_init(tifm_sd_init); +module_exit(tifm_sd_exit); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index c27e782e6d..30294127a0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -52,3 +52,11 @@ config PCI_DEBUG When in doubt, say N. +config HT_IRQ + bool "Interrupts on hypertransport devices" + default y + depends on X86_LOCAL_APIC && X86_IO_APIC + help + This allows native hypertransport devices to use interrupts. + + If unsure say Y. diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index f2d152b818..e3beb78440 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -14,6 +14,12 @@ obj-$(CONFIG_HOTPLUG) += hotplug.o # Build the PCI Hotplug drivers if we were asked to obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ +# Build the PCI MSI interrupt support +obj-$(CONFIG_PCI_MSI) += msi.o + +# Build the Hypertransport interrupt support +obj-$(CONFIG_HT_IRQ) += htirq.o + # # Some architectures use the generic PCI setup functions # @@ -27,11 +33,6 @@ obj-$(CONFIG_PPC64) += setup-bus.o obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o -msiobj-y := msi.o msi-apic.o -msiobj-$(CONFIG_IA64_GENERIC) += msi-altix.o -msiobj-$(CONFIG_IA64_SGI_SN2) += msi-altix.o -obj-$(CONFIG_PCI_MSI) += $(msiobj-y) - # # ACPI Related PCI FW Functions # diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c new file mode 100644 index 0000000000..0e27f2404a --- /dev/null +++ b/drivers/pci/htirq.c @@ -0,0 +1,190 @@ +/* + * File: htirq.c + * Purpose: Hypertransport Interrupt Capability + * + * Copyright (C) 2006 Linux Networx + * Copyright (C) Eric Biederman + */ + +#include +#include +#include +#include +#include +#include + +/* Global ht irq lock. + * + * This is needed to serialize access to the data port in hypertransport + * irq capability. + * + * With multiple simultaneous hypertransport irq devices it might pay + * to make this more fine grained. But start with simple, stupid, and correct. + */ +static DEFINE_SPINLOCK(ht_irq_lock); + +struct ht_irq_cfg { + struct pci_dev *dev; + unsigned pos; + unsigned idx; +}; + +void write_ht_irq_low(unsigned int irq, u32 data) +{ + struct ht_irq_cfg *cfg = get_irq_data(irq); + unsigned long flags; + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); + pci_write_config_dword(cfg->dev, cfg->pos + 4, data); + spin_unlock_irqrestore(&ht_irq_lock, flags); +} + +void write_ht_irq_high(unsigned int irq, u32 data) +{ + struct ht_irq_cfg *cfg = get_irq_data(irq); + unsigned long flags; + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1); + pci_write_config_dword(cfg->dev, cfg->pos + 4, data); + spin_unlock_irqrestore(&ht_irq_lock, flags); +} + +u32 read_ht_irq_low(unsigned int irq) +{ + struct ht_irq_cfg *cfg = get_irq_data(irq); + unsigned long flags; + u32 data; + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); + pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); + spin_unlock_irqrestore(&ht_irq_lock, flags); + return data; +} + +u32 read_ht_irq_high(unsigned int irq) +{ + struct ht_irq_cfg *cfg = get_irq_data(irq); + unsigned long flags; + u32 data; + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1); + pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); + spin_unlock_irqrestore(&ht_irq_lock, flags); + return data; +} + +void mask_ht_irq(unsigned int irq) +{ + struct ht_irq_cfg *cfg; + unsigned long flags; + u32 data; + + cfg = get_irq_data(irq); + + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); + pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); + data |= 1; + pci_write_config_dword(cfg->dev, cfg->pos + 4, data); + spin_unlock_irqrestore(&ht_irq_lock, flags); +} + +void unmask_ht_irq(unsigned int irq) +{ + struct ht_irq_cfg *cfg; + unsigned long flags; + u32 data; + + cfg = get_irq_data(irq); + + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); + pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); + data &= ~1; + pci_write_config_dword(cfg->dev, cfg->pos + 4, data); + spin_unlock_irqrestore(&ht_irq_lock, flags); +} + +/** + * ht_create_irq - create an irq and attach it to a device. + * @dev: The hypertransport device to find the irq capability on. + * @idx: Which of the possible irqs to attach to. + * + * ht_create_irq is needs to be called for all hypertransport devices + * that generate irqs. + * + * The irq number of the new irq or a negative error value is returned. + */ +int ht_create_irq(struct pci_dev *dev, int idx) +{ + struct ht_irq_cfg *cfg; + unsigned long flags; + u32 data; + int max_irq; + int pos; + int irq; + + pos = pci_find_capability(dev, PCI_CAP_ID_HT); + while (pos) { + u8 subtype; + pci_read_config_byte(dev, pos + 3, &subtype); + if (subtype == HT_CAPTYPE_IRQ) + break; + pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT); + } + if (!pos) + return -EINVAL; + + /* Verify the idx I want to use is in range */ + spin_lock_irqsave(&ht_irq_lock, flags); + pci_write_config_byte(dev, pos + 2, 1); + pci_read_config_dword(dev, pos + 4, &data); + spin_unlock_irqrestore(&ht_irq_lock, flags); + + max_irq = (data >> 16) & 0xff; + if ( idx > max_irq) + return -EINVAL; + + cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->dev = dev; + cfg->pos = pos; + cfg->idx = 0x10 + (idx * 2); + + irq = create_irq(); + if (irq < 0) { + kfree(cfg); + return -EBUSY; + } + set_irq_data(irq, cfg); + + if (arch_setup_ht_irq(irq, dev) < 0) { + ht_destroy_irq(irq); + return -EBUSY; + } + + return irq; +} + +/** + * ht_destroy_irq - destroy an irq created with ht_create_irq + * + * This reverses ht_create_irq removing the specified irq from + * existence. The irq should be free before this happens. + */ +void ht_destroy_irq(unsigned int irq) +{ + struct ht_irq_cfg *cfg; + + cfg = get_irq_data(irq); + set_irq_chip(irq, NULL); + set_irq_data(irq, NULL); + destroy_irq(irq); + + kfree(cfg); +} + +EXPORT_SYMBOL(ht_create_irq); +EXPORT_SYMBOL(ht_destroy_irq); diff --git a/drivers/pci/msi-apic.c b/drivers/pci/msi-apic.c deleted file mode 100644 index 5ed798b319..0000000000 --- a/drivers/pci/msi-apic.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * MSI hooks for standard x86 apic - */ - -#include -#include -#include - -#include "msi.h" - -/* - * Shifts for APIC-based data - */ - -#define MSI_DATA_VECTOR_SHIFT 0 -#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) - -#define MSI_DATA_DELIVERY_SHIFT 8 -#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) -#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) - -#define MSI_DATA_LEVEL_SHIFT 14 -#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) -#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) - -#define MSI_DATA_TRIGGER_SHIFT 15 -#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) -#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) - -/* - * Shift/mask fields for APIC-based bus address - */ - -#define MSI_ADDR_HEADER 0xfee00000 - -#define MSI_ADDR_DESTID_MASK 0xfff0000f -#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) - -#define MSI_ADDR_DESTMODE_SHIFT 2 -#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) -#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) - -#define MSI_ADDR_REDIRECTION_SHIFT 3 -#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) -#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) - - -static void -msi_target_apic(unsigned int vector, - unsigned int dest_cpu, - u32 *address_hi, /* in/out */ - u32 *address_lo) /* in/out */ -{ - u32 addr = *address_lo; - - addr &= MSI_ADDR_DESTID_MASK; - addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(dest_cpu)); - - *address_lo = addr; -} - -static int -msi_setup_apic(struct pci_dev *pdev, /* unused in generic */ - unsigned int vector, - u32 *address_hi, - u32 *address_lo, - u32 *data) -{ - unsigned long dest_phys_id; - - dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); - - *address_hi = 0; - *address_lo = MSI_ADDR_HEADER | - MSI_ADDR_DESTMODE_PHYS | - MSI_ADDR_REDIRECTION_CPU | - MSI_ADDR_DESTID_CPU(dest_phys_id); - - *data = MSI_DATA_TRIGGER_EDGE | - MSI_DATA_LEVEL_ASSERT | - MSI_DATA_DELIVERY_FIXED | - MSI_DATA_VECTOR(vector); - - return 0; -} - -static void -msi_teardown_apic(unsigned int vector) -{ - return; /* no-op */ -} - -/* - * Generic ops used on most IA archs/platforms. Set with msi_register() - */ - -struct msi_ops msi_apic_ops = { - .setup = msi_setup_apic, - .teardown = msi_teardown_apic, - .target = msi_target_apic, -}; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 27a057409e..f9fdc54473 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -6,6 +6,7 @@ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) */ +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -27,23 +29,6 @@ static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; static kmem_cache_t* msi_cachep; static int pci_msi_enable = 1; -static int last_alloc_vector; -static int nr_released_vectors; -static int nr_reserved_vectors = NR_HP_RESERVED_VECTORS; -static int nr_msix_devices; - -#ifndef CONFIG_X86_IO_APIC -int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1}; -#endif - -static struct msi_ops *msi_ops; - -int -msi_register(struct msi_ops *ops) -{ - msi_ops = ops; - return 0; -} static int msi_cache_init(void) { @@ -55,26 +40,25 @@ static int msi_cache_init(void) return 0; } -static void msi_set_mask_bit(unsigned int vector, int flag) +static void msi_set_mask_bit(unsigned int irq, int flag) { struct msi_desc *entry; - entry = (struct msi_desc *)msi_desc[vector]; - if (!entry || !entry->dev || !entry->mask_base) - return; + entry = msi_desc[irq]; + BUG_ON(!entry || !entry->dev); switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: - { - int pos; - u32 mask_bits; - - pos = (long)entry->mask_base; - pci_read_config_dword(entry->dev, pos, &mask_bits); - mask_bits &= ~(1); - mask_bits |= flag; - pci_write_config_dword(entry->dev, pos, mask_bits); + if (entry->msi_attrib.maskbit) { + int pos; + u32 mask_bits; + + pos = (long)entry->mask_base; + pci_read_config_dword(entry->dev, pos, &mask_bits); + mask_bits &= ~(1); + mask_bits |= flag; + pci_write_config_dword(entry->dev, pos, mask_bits); + } break; - } case PCI_CAP_ID_MSIX: { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + @@ -83,261 +67,101 @@ static void msi_set_mask_bit(unsigned int vector, int flag) break; } default: + BUG(); break; } } -#ifdef CONFIG_SMP -static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) +void read_msi_msg(unsigned int irq, struct msi_msg *msg) { - struct msi_desc *entry; - u32 address_hi, address_lo; - unsigned int irq = vector; - unsigned int dest_cpu = first_cpu(cpu_mask); - - entry = (struct msi_desc *)msi_desc[vector]; - if (!entry || !entry->dev) - return; - - switch (entry->msi_attrib.type) { + struct msi_desc *entry = get_irq_data(irq); + switch(entry->msi_attrib.type) { case PCI_CAP_ID_MSI: { - int pos = pci_find_capability(entry->dev, PCI_CAP_ID_MSI); - - if (!pos) - return; - - pci_read_config_dword(entry->dev, msi_upper_address_reg(pos), - &address_hi); - pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), - &address_lo); - - msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); - - pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), - address_hi); - pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), - address_lo); - set_native_irq_info(irq, cpu_mask); + struct pci_dev *dev = entry->dev; + int pos = entry->msi_attrib.pos; + u16 data; + + pci_read_config_dword(dev, msi_lower_address_reg(pos), + &msg->address_lo); + if (entry->msi_attrib.is_64) { + pci_read_config_dword(dev, msi_upper_address_reg(pos), + &msg->address_hi); + pci_read_config_word(dev, msi_data_reg(pos, 1), &data); + } else { + msg->address_hi = 0; + pci_read_config_word(dev, msi_data_reg(pos, 1), &data); + } + msg->data = data; break; } case PCI_CAP_ID_MSIX: { - int offset_hi = - entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET; - int offset_lo = - entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; - - address_hi = readl(entry->mask_base + offset_hi); - address_lo = readl(entry->mask_base + offset_lo); + void __iomem *base; + base = entry->mask_base + + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; - msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); + msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); + break; + } + default: + BUG(); + } +} - writel(address_hi, entry->mask_base + offset_hi); - writel(address_lo, entry->mask_base + offset_lo); - set_native_irq_info(irq, cpu_mask); +void write_msi_msg(unsigned int irq, struct msi_msg *msg) +{ + struct msi_desc *entry = get_irq_data(irq); + switch (entry->msi_attrib.type) { + case PCI_CAP_ID_MSI: + { + struct pci_dev *dev = entry->dev; + int pos = entry->msi_attrib.pos; + + pci_write_config_dword(dev, msi_lower_address_reg(pos), + msg->address_lo); + if (entry->msi_attrib.is_64) { + pci_write_config_dword(dev, msi_upper_address_reg(pos), + msg->address_hi); + pci_write_config_word(dev, msi_data_reg(pos, 1), + msg->data); + } else { + pci_write_config_word(dev, msi_data_reg(pos, 0), + msg->data); + } break; } - default: + case PCI_CAP_ID_MSIX: + { + void __iomem *base; + base = entry->mask_base + + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; + + writel(msg->address_lo, + base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(msg->address_hi, + base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET); break; } -} -#else -#define set_msi_affinity NULL -#endif /* CONFIG_SMP */ - -static void mask_MSI_irq(unsigned int vector) -{ - msi_set_mask_bit(vector, 1); -} - -static void unmask_MSI_irq(unsigned int vector) -{ - msi_set_mask_bit(vector, 0); -} - -static unsigned int startup_msi_irq_wo_maskbit(unsigned int vector) -{ - struct msi_desc *entry; - unsigned long flags; - - spin_lock_irqsave(&msi_lock, flags); - entry = msi_desc[vector]; - if (!entry || !entry->dev) { - spin_unlock_irqrestore(&msi_lock, flags); - return 0; + default: + BUG(); } - entry->msi_attrib.state = 1; /* Mark it active */ - spin_unlock_irqrestore(&msi_lock, flags); - - return 0; /* never anything pending */ } -static unsigned int startup_msi_irq_w_maskbit(unsigned int vector) +void mask_msi_irq(unsigned int irq) { - startup_msi_irq_wo_maskbit(vector); - unmask_MSI_irq(vector); - return 0; /* never anything pending */ -} - -static void shutdown_msi_irq(unsigned int vector) -{ - struct msi_desc *entry; - unsigned long flags; - - spin_lock_irqsave(&msi_lock, flags); - entry = msi_desc[vector]; - if (entry && entry->dev) - entry->msi_attrib.state = 0; /* Mark it not active */ - spin_unlock_irqrestore(&msi_lock, flags); + msi_set_mask_bit(irq, 1); } -static void end_msi_irq_wo_maskbit(unsigned int vector) +void unmask_msi_irq(unsigned int irq) { - move_native_irq(vector); - ack_APIC_irq(); -} - -static void end_msi_irq_w_maskbit(unsigned int vector) -{ - move_native_irq(vector); - unmask_MSI_irq(vector); - ack_APIC_irq(); -} - -static void do_nothing(unsigned int vector) -{ -} - -/* - * Interrupt Type for MSI-X PCI/PCI-X/PCI-Express Devices, - * which implement the MSI-X Capability Structure. - */ -static struct hw_interrupt_type msix_irq_type = { - .typename = "PCI-MSI-X", - .startup = startup_msi_irq_w_maskbit, - .shutdown = shutdown_msi_irq, - .enable = unmask_MSI_irq, - .disable = mask_MSI_irq, - .ack = mask_MSI_irq, - .end = end_msi_irq_w_maskbit, - .set_affinity = set_msi_affinity -}; - -/* - * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, - * which implement the MSI Capability Structure with - * Mask-and-Pending Bits. - */ -static struct hw_interrupt_type msi_irq_w_maskbit_type = { - .typename = "PCI-MSI", - .startup = startup_msi_irq_w_maskbit, - .shutdown = shutdown_msi_irq, - .enable = unmask_MSI_irq, - .disable = mask_MSI_irq, - .ack = mask_MSI_irq, - .end = end_msi_irq_w_maskbit, - .set_affinity = set_msi_affinity -}; - -/* - * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, - * which implement the MSI Capability Structure without - * Mask-and-Pending Bits. - */ -static struct hw_interrupt_type msi_irq_wo_maskbit_type = { - .typename = "PCI-MSI", - .startup = startup_msi_irq_wo_maskbit, - .shutdown = shutdown_msi_irq, - .enable = do_nothing, - .disable = do_nothing, - .ack = do_nothing, - .end = end_msi_irq_wo_maskbit, - .set_affinity = set_msi_affinity -}; - -static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); -static int assign_msi_vector(void) -{ - static int new_vector_avail = 1; - int vector; - unsigned long flags; - - /* - * msi_lock is provided to ensure that successful allocation of MSI - * vector is assigned unique among drivers. - */ - spin_lock_irqsave(&msi_lock, flags); - - if (!new_vector_avail) { - int free_vector = 0; - - /* - * vector_irq[] = -1 indicates that this specific vector is: - * - assigned for MSI (since MSI have no associated IRQ) or - * - assigned for legacy if less than 16, or - * - having no corresponding 1:1 vector-to-IOxAPIC IRQ mapping - * vector_irq[] = 0 indicates that this vector, previously - * assigned for MSI, is freed by hotplug removed operations. - * This vector will be reused for any subsequent hotplug added - * operations. - * vector_irq[] > 0 indicates that this vector is assigned for - * IOxAPIC IRQs. This vector and its value provides a 1-to-1 - * vector-to-IOxAPIC IRQ mapping. - */ - for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) { - if (vector_irq[vector] != 0) - continue; - free_vector = vector; - if (!msi_desc[vector]) - break; - else - continue; - } - if (!free_vector) { - spin_unlock_irqrestore(&msi_lock, flags); - return -EBUSY; - } - vector_irq[free_vector] = -1; - nr_released_vectors--; - spin_unlock_irqrestore(&msi_lock, flags); - if (msi_desc[free_vector] != NULL) { - struct pci_dev *dev; - int tail; - - /* free all linked vectors before re-assign */ - do { - spin_lock_irqsave(&msi_lock, flags); - dev = msi_desc[free_vector]->dev; - tail = msi_desc[free_vector]->link.tail; - spin_unlock_irqrestore(&msi_lock, flags); - msi_free_vector(dev, tail, 1); - } while (free_vector != tail); - } - - return free_vector; - } - vector = assign_irq_vector(AUTO_ASSIGN); - last_alloc_vector = vector; - if (vector == LAST_DEVICE_VECTOR) - new_vector_avail = 0; - - spin_unlock_irqrestore(&msi_lock, flags); - return vector; -} - -static int get_new_vector(void) -{ - int vector = assign_msi_vector(); - - if (vector > 0) - set_intr_gate(vector, interrupt[vector]); - - return vector; + msi_set_mask_bit(irq, 0); } +static int msi_free_irq(struct pci_dev* dev, int irq); static int msi_init(void) { static int status = -ENOMEM; @@ -352,22 +176,6 @@ static int msi_init(void) return status; } - status = msi_arch_init(); - if (status < 0) { - pci_msi_enable = 0; - printk(KERN_WARNING - "PCI: MSI arch init failed. MSI disabled.\n"); - return status; - } - - if (! msi_ops) { - printk(KERN_WARNING - "PCI: MSI ops not registered. MSI disabled.\n"); - status = -EINVAL; - return status; - } - - last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); status = msi_cache_init(); if (status < 0) { pci_msi_enable = 0; @@ -375,23 +183,9 @@ static int msi_init(void) return status; } - if (last_alloc_vector < 0) { - pci_msi_enable = 0; - printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); - status = -EBUSY; - return status; - } - vector_irq[last_alloc_vector] = 0; - nr_released_vectors++; - return status; } -static int get_msi_vector(struct pci_dev *dev) -{ - return get_new_vector(); -} - static struct msi_desc* alloc_msi_entry(void) { struct msi_desc *entry; @@ -406,29 +200,44 @@ static struct msi_desc* alloc_msi_entry(void) return entry; } -static void attach_msi_entry(struct msi_desc *entry, int vector) +static void attach_msi_entry(struct msi_desc *entry, int irq) { unsigned long flags; spin_lock_irqsave(&msi_lock, flags); - msi_desc[vector] = entry; + msi_desc[irq] = entry; spin_unlock_irqrestore(&msi_lock, flags); } -static void irq_handler_init(int cap_id, int pos, int mask) +static int create_msi_irq(void) { - unsigned long flags; + struct msi_desc *entry; + int irq; - spin_lock_irqsave(&irq_desc[pos].lock, flags); - if (cap_id == PCI_CAP_ID_MSIX) - irq_desc[pos].chip = &msix_irq_type; - else { - if (!mask) - irq_desc[pos].chip = &msi_irq_wo_maskbit_type; - else - irq_desc[pos].chip = &msi_irq_w_maskbit_type; + entry = alloc_msi_entry(); + if (!entry) + return -ENOMEM; + + irq = create_irq(); + if (irq < 0) { + kmem_cache_free(msi_cachep, entry); + return -EBUSY; } - spin_unlock_irqrestore(&irq_desc[pos].lock, flags); + + set_irq_data(irq, entry); + + return irq; +} + +static void destroy_msi_irq(unsigned int irq) +{ + struct msi_desc *entry; + + entry = get_irq_data(irq); + set_irq_chip(irq, NULL); + set_irq_data(irq, NULL); + destroy_irq(irq); + kmem_cache_free(msi_cachep, entry); } static void enable_msi_mode(struct pci_dev *dev, int pos, int type) @@ -473,21 +282,21 @@ void disable_msi_mode(struct pci_dev *dev, int pos, int type) } } -static int msi_lookup_vector(struct pci_dev *dev, int type) +static int msi_lookup_irq(struct pci_dev *dev, int type) { - int vector; + int irq; unsigned long flags; spin_lock_irqsave(&msi_lock, flags); - for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) { - if (!msi_desc[vector] || msi_desc[vector]->dev != dev || - msi_desc[vector]->msi_attrib.type != type || - msi_desc[vector]->msi_attrib.default_vector != dev->irq) + for (irq = 0; irq < NR_IRQS; irq++) { + if (!msi_desc[irq] || msi_desc[irq]->dev != dev || + msi_desc[irq]->msi_attrib.type != type || + msi_desc[irq]->msi_attrib.default_irq != dev->irq) continue; spin_unlock_irqrestore(&msi_lock, flags); - /* This pre-assigned MSI vector for this device - already exits. Override dev->irq with this vector */ - dev->irq = vector; + /* This pre-assigned MSI irq for this device + already exits. Override dev->irq with this irq */ + dev->irq = irq; return 0; } spin_unlock_irqrestore(&msi_lock, flags); @@ -499,11 +308,6 @@ void pci_scan_msi_device(struct pci_dev *dev) { if (!dev) return; - - if (pci_find_capability(dev, PCI_CAP_ID_MSIX) > 0) - nr_msix_devices++; - else if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0) - nr_reserved_vectors++; } #ifdef CONFIG_PM @@ -577,7 +381,7 @@ int pci_save_msix_state(struct pci_dev *dev) { int pos; int temp; - int vector, head, tail = 0; + int irq, head, tail = 0; u16 control; struct pci_cap_saved_state *save_state; @@ -599,33 +403,20 @@ int pci_save_msix_state(struct pci_dev *dev) /* save the table */ temp = dev->irq; - if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { + if (msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { kfree(save_state); return -EINVAL; } - vector = head = dev->irq; + irq = head = dev->irq; while (head != tail) { - int j; - void __iomem *base; struct msi_desc *entry; - entry = msi_desc[vector]; - base = entry->mask_base; - j = entry->msi_attrib.entry_nr; - - entry->address_lo_save = - readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - entry->address_hi_save = - readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - entry->data_save = - readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_DATA_OFFSET); - - tail = msi_desc[vector]->link.tail; - vector = tail; + entry = msi_desc[irq]; + read_msi_msg(irq, &entry->msg_save); + + tail = msi_desc[irq]->link.tail; + irq = tail; } dev->irq = temp; @@ -638,9 +429,7 @@ void pci_restore_msix_state(struct pci_dev *dev) { u16 save; int pos; - int vector, head, tail = 0; - void __iomem *base; - int j; + int irq, head, tail = 0; struct msi_desc *entry; int temp; struct pci_cap_saved_state *save_state; @@ -658,26 +447,15 @@ void pci_restore_msix_state(struct pci_dev *dev) /* route the table */ temp = dev->irq; - if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) + if (msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) return; - vector = head = dev->irq; + irq = head = dev->irq; while (head != tail) { - entry = msi_desc[vector]; - base = entry->mask_base; - j = entry->msi_attrib.entry_nr; - - writel(entry->address_lo_save, - base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel(entry->address_hi_save, - base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel(entry->data_save, - base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_DATA_OFFSET); - - tail = msi_desc[vector]->link.tail; - vector = tail; + entry = msi_desc[irq]; + write_msi_msg(irq, &entry->msg_save); + + tail = msi_desc[irq]->link.tail; + irq = tail; } dev->irq = temp; @@ -686,104 +464,68 @@ void pci_restore_msix_state(struct pci_dev *dev) } #endif -static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry) -{ - int status; - u32 address_hi; - u32 address_lo; - u32 data; - int pos, vector = dev->irq; - u16 control; - - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - pci_read_config_word(dev, msi_control_reg(pos), &control); - - /* Configure MSI capability structure */ - status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data); - if (status < 0) - return status; - - pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo); - if (is_64bit_address(control)) { - pci_write_config_dword(dev, - msi_upper_address_reg(pos), address_hi); - pci_write_config_word(dev, - msi_data_reg(pos, 1), data); - } else - pci_write_config_word(dev, - msi_data_reg(pos, 0), data); - if (entry->msi_attrib.maskbit) { - unsigned int maskbits, temp; - /* All MSIs are unmasked by default, Mask them all */ - pci_read_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - &maskbits); - temp = (1 << multi_msi_capable(control)); - temp = ((temp - 1) & ~temp); - maskbits |= temp; - pci_write_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - maskbits); - } - - return 0; -} - /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with a single - * MSI vector, regardless of device function is capable of handling + * MSI irq, regardless of device function is capable of handling * multiple messages. A return of zero indicates the successful setup - * of an entry zero with the new MSI vector or non-zero for otherwise. + * of an entry zero with the new MSI irq or non-zero for otherwise. **/ static int msi_capability_init(struct pci_dev *dev) { int status; struct msi_desc *entry; - int pos, vector; + int pos, irq; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSI); pci_read_config_word(dev, msi_control_reg(pos), &control); /* MSI Entry Initialization */ - entry = alloc_msi_entry(); - if (!entry) - return -ENOMEM; + irq = create_msi_irq(); + if (irq < 0) + return irq; - vector = get_msi_vector(dev); - if (vector < 0) { - kmem_cache_free(msi_cachep, entry); - return -EBUSY; - } - entry->link.head = vector; - entry->link.tail = vector; + entry = get_irq_data(irq); + entry->link.head = irq; + entry->link.tail = irq; entry->msi_attrib.type = PCI_CAP_ID_MSI; - entry->msi_attrib.state = 0; /* Mark it not active */ + entry->msi_attrib.is_64 = is_64bit_address(control); entry->msi_attrib.entry_nr = 0; entry->msi_attrib.maskbit = is_mask_bit_support(control); - entry->msi_attrib.default_vector = dev->irq; /* Save IOAPIC IRQ */ - dev->irq = vector; - entry->dev = dev; + entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ + entry->msi_attrib.pos = pos; if (is_mask_bit_support(control)) { entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos, is_64bit_address(control)); } - /* Replace with MSI handler */ - irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); + entry->dev = dev; + if (entry->msi_attrib.maskbit) { + unsigned int maskbits, temp; + /* All MSIs are unmasked by default, Mask them all */ + pci_read_config_dword(dev, + msi_mask_bits_reg(pos, is_64bit_address(control)), + &maskbits); + temp = (1 << multi_msi_capable(control)); + temp = ((temp - 1) & ~temp); + maskbits |= temp; + pci_write_config_dword(dev, + msi_mask_bits_reg(pos, is_64bit_address(control)), + maskbits); + } /* Configure MSI capability structure */ - status = msi_register_init(dev, entry); - if (status != 0) { - dev->irq = entry->msi_attrib.default_vector; - kmem_cache_free(msi_cachep, entry); + status = arch_setup_msi_irq(irq, dev); + if (status < 0) { + destroy_msi_irq(irq); return status; } - attach_msi_entry(entry, vector); + attach_msi_entry(entry, irq); /* Set MSI enabled bits */ enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + dev->irq = irq; return 0; } @@ -794,18 +536,15 @@ static int msi_capability_init(struct pci_dev *dev) * @nvec: number of @entries * * Setup the MSI-X capability structure of device function with a - * single MSI-X vector. A return of zero indicates the successful setup of - * requested MSI-X entries with allocated vectors or non-zero for otherwise. + * single MSI-X irq. A return of zero indicates the successful setup of + * requested MSI-X entries with allocated irqs or non-zero for otherwise. **/ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; - u32 address_hi; - u32 address_lo; - u32 data; int status; - int vector, pos, i, j, nr_entries, temp = 0; + int irq, pos, i, j, nr_entries, temp = 0; unsigned long phys_addr; u32 table_offset; u16 control; @@ -827,65 +566,56 @@ static int msix_capability_init(struct pci_dev *dev, /* MSI-X Table Initialization */ for (i = 0; i < nvec; i++) { - entry = alloc_msi_entry(); - if (!entry) + irq = create_msi_irq(); + if (irq < 0) break; - vector = get_msi_vector(dev); - if (vector < 0) { - kmem_cache_free(msi_cachep, entry); - break; - } + entry = get_irq_data(irq); j = entries[i].entry; - entries[i].vector = vector; + entries[i].vector = irq; entry->msi_attrib.type = PCI_CAP_ID_MSIX; - entry->msi_attrib.state = 0; /* Mark it not active */ + entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = j; entry->msi_attrib.maskbit = 1; - entry->msi_attrib.default_vector = dev->irq; + entry->msi_attrib.default_irq = dev->irq; + entry->msi_attrib.pos = pos; entry->dev = dev; entry->mask_base = base; if (!head) { - entry->link.head = vector; - entry->link.tail = vector; + entry->link.head = irq; + entry->link.tail = irq; head = entry; } else { entry->link.head = temp; entry->link.tail = tail->link.tail; - tail->link.tail = vector; - head->link.head = vector; + tail->link.tail = irq; + head->link.head = irq; } - temp = vector; + temp = irq; tail = entry; - /* Replace with MSI-X handler */ - irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); /* Configure MSI-X capability structure */ - status = msi_ops->setup(dev, vector, - &address_hi, - &address_lo, - &data); - if (status < 0) + status = arch_setup_msi_irq(irq, dev); + if (status < 0) { + destroy_msi_irq(irq); break; + } - writel(address_lo, - base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel(address_hi, - base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel(data, - base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_DATA_OFFSET); - attach_msi_entry(entry, vector); + attach_msi_entry(entry, irq); } if (i != nvec) { + int avail = i - 1; i--; for (; i >= 0; i--) { - vector = (entries + i)->vector; - msi_free_vector(dev, vector, 0); + irq = (entries + i)->vector; + msi_free_irq(dev, irq); (entries + i)->vector = 0; } - return -EBUSY; + /* If we had some success report the number of irqs + * we succeeded in setting up. + */ + if (avail <= 0) + avail = -EBUSY; + return avail; } /* Set MSI-X enabled bits */ enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); @@ -925,15 +655,14 @@ int pci_msi_supported(struct pci_dev * dev) * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with - * a single MSI vector upon its software driver call to request for + * a single MSI irq upon its software driver call to request for * MSI mode enabled on its hardware device function. A return of zero * indicates the successful setup of an entry zero with the new MSI - * vector or non-zero for otherwise. + * irq or non-zero for otherwise. **/ int pci_enable_msi(struct pci_dev* dev) { int pos, temp, status; - u16 control; if (pci_msi_supported(dev) < 0) return -EINVAL; @@ -948,52 +677,25 @@ int pci_enable_msi(struct pci_dev* dev) if (!pos) return -EINVAL; - if (!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { - /* Lookup Sucess */ - unsigned long flags; + WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSI)); - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (control & PCI_MSI_FLAGS_ENABLE) - return 0; /* Already in MSI mode */ - spin_lock_irqsave(&msi_lock, flags); - if (!vector_irq[dev->irq]) { - msi_desc[dev->irq]->msi_attrib.state = 0; - vector_irq[dev->irq] = -1; - nr_released_vectors--; - spin_unlock_irqrestore(&msi_lock, flags); - status = msi_register_init(dev, msi_desc[dev->irq]); - if (status == 0) - enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); - return status; - } - spin_unlock_irqrestore(&msi_lock, flags); - dev->irq = temp; - } - /* Check whether driver already requested for MSI-X vectors */ + /* Check whether driver already requested for MSI-X irqs */ pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { + if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { printk(KERN_INFO "PCI: %s: Can't enable MSI. " - "Device already has MSI-X vectors assigned\n", + "Device already has MSI-X irq assigned\n", pci_name(dev)); dev->irq = temp; return -EINVAL; } status = msi_capability_init(dev); - if (!status) { - if (!pos) - nr_reserved_vectors--; /* Only MSI capable */ - else if (nr_msix_devices > 0) - nr_msix_devices--; /* Both MSI and MSI-X capable, - but choose enabling MSI */ - } - return status; } void pci_disable_msi(struct pci_dev* dev) { struct msi_desc *entry; - int pos, default_vector; + int pos, default_irq; u16 control; unsigned long flags; @@ -1010,41 +712,41 @@ void pci_disable_msi(struct pci_dev* dev) if (!(control & PCI_MSI_FLAGS_ENABLE)) return; + disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + spin_lock_irqsave(&msi_lock, flags); entry = msi_desc[dev->irq]; if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) { spin_unlock_irqrestore(&msi_lock, flags); return; } - if (entry->msi_attrib.state) { + if (irq_has_action(dev->irq)) { spin_unlock_irqrestore(&msi_lock, flags); printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without " - "free_irq() on MSI vector %d\n", + "free_irq() on MSI irq %d\n", pci_name(dev), dev->irq); - BUG_ON(entry->msi_attrib.state > 0); + BUG_ON(irq_has_action(dev->irq)); } else { - vector_irq[dev->irq] = 0; /* free it */ - nr_released_vectors++; - default_vector = entry->msi_attrib.default_vector; + default_irq = entry->msi_attrib.default_irq; spin_unlock_irqrestore(&msi_lock, flags); - /* Restore dev->irq to its default pin-assertion vector */ - dev->irq = default_vector; - disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), - PCI_CAP_ID_MSI); + msi_free_irq(dev, dev->irq); + + /* Restore dev->irq to its default pin-assertion irq */ + dev->irq = default_irq; } } -static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) +static int msi_free_irq(struct pci_dev* dev, int irq) { struct msi_desc *entry; int head, entry_nr, type; void __iomem *base; unsigned long flags; - msi_ops->teardown(vector); + arch_teardown_msi_irq(irq); spin_lock_irqsave(&msi_lock, flags); - entry = msi_desc[vector]; + entry = msi_desc[irq]; if (!entry || entry->dev != dev) { spin_unlock_irqrestore(&msi_lock, flags); return -EINVAL; @@ -1056,100 +758,42 @@ static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) msi_desc[entry->link.head]->link.tail = entry->link.tail; msi_desc[entry->link.tail]->link.head = entry->link.head; entry->dev = NULL; - if (!reassign) { - vector_irq[vector] = 0; - nr_released_vectors++; - } - msi_desc[vector] = NULL; + msi_desc[irq] = NULL; spin_unlock_irqrestore(&msi_lock, flags); - kmem_cache_free(msi_cachep, entry); + destroy_msi_irq(irq); if (type == PCI_CAP_ID_MSIX) { - if (!reassign) - writel(1, base + - entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); + writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); - if (head == vector) + if (head == irq) iounmap(base); } return 0; } -static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec) -{ - int vector = head, tail = 0; - int i, j = 0, nr_entries = 0; - void __iomem *base; - unsigned long flags; - - spin_lock_irqsave(&msi_lock, flags); - while (head != tail) { - nr_entries++; - tail = msi_desc[vector]->link.tail; - if (entries[0].entry == msi_desc[vector]->msi_attrib.entry_nr) - j = vector; - vector = tail; - } - if (*nvec > nr_entries) { - spin_unlock_irqrestore(&msi_lock, flags); - *nvec = nr_entries; - return -EINVAL; - } - vector = ((j > 0) ? j : head); - for (i = 0; i < *nvec; i++) { - j = msi_desc[vector]->msi_attrib.entry_nr; - msi_desc[vector]->msi_attrib.state = 0; /* Mark it not active */ - vector_irq[vector] = -1; /* Mark it busy */ - nr_released_vectors--; - entries[i].vector = vector; - if (j != (entries + i)->entry) { - base = msi_desc[vector]->mask_base; - msi_desc[vector]->msi_attrib.entry_nr = - (entries + i)->entry; - writel( readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET), base + - (entries + i)->entry * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel( readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET), base + - (entries + i)->entry * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel( (readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_DATA_OFFSET) & 0xff00) | vector, - base + (entries+i)->entry*PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_DATA_OFFSET); - } - vector = msi_desc[vector]->link.tail; - } - spin_unlock_irqrestore(&msi_lock, flags); - - return 0; -} - /** * pci_enable_msix - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of MSI-X entries - * @nvec: number of MSI-X vectors requested for allocation by device driver + * @nvec: number of MSI-X irqs requested for allocation by device driver * * Setup the MSI-X capability structure of device function with the number - * of requested vectors upon its software driver call to request for + * of requested irqs upon its software driver call to request for * MSI-X mode enabled on its hardware device function. A return of zero * indicates the successful configuration of MSI-X capability structure - * with new allocated MSI-X vectors. A return of < 0 indicates a failure. + * with new allocated MSI-X irqs. A return of < 0 indicates a failure. * Or a return of > 0 indicates that driver request is exceeding the number - * of vectors available. Driver should use the returned value to re-send + * of irqs available. Driver should use the returned value to re-send * its request. **/ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) { - int status, pos, nr_entries, free_vectors; + int status, pos, nr_entries; int i, j, temp; u16 control; - unsigned long flags; if (!entries || pci_msi_supported(dev) < 0) return -EINVAL; @@ -1163,9 +807,6 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) return -EINVAL; pci_read_config_word(dev, msi_control_reg(pos), &control); - if (control & PCI_MSIX_FLAGS_ENABLE) - return -EINVAL; /* Already in MSI-X mode */ - nr_entries = multi_msix_capable(control); if (nvec > nr_entries) return -EINVAL; @@ -1180,56 +821,18 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) } } temp = dev->irq; - if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { - /* Lookup Sucess */ - nr_entries = nvec; - /* Reroute MSI-X table */ - if (reroute_msix_table(dev->irq, entries, &nr_entries)) { - /* #requested > #previous-assigned */ - dev->irq = temp; - return nr_entries; - } - dev->irq = temp; - enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); - return 0; - } - /* Check whether driver already requested for MSI vector */ + WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSIX)); + + /* Check whether driver already requested for MSI irq */ if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 && - !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { + !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) { printk(KERN_INFO "PCI: %s: Can't enable MSI-X. " - "Device already has an MSI vector assigned\n", + "Device already has an MSI irq assigned\n", pci_name(dev)); dev->irq = temp; return -EINVAL; } - - spin_lock_irqsave(&msi_lock, flags); - /* - * msi_lock is provided to ensure that enough vectors resources are - * available before granting. - */ - free_vectors = pci_vector_resources(last_alloc_vector, - nr_released_vectors); - /* Ensure that each MSI/MSI-X device has one vector reserved by - default to avoid any MSI-X driver to take all available - resources */ - free_vectors -= nr_reserved_vectors; - /* Find the average of free vectors among MSI-X devices */ - if (nr_msix_devices > 0) - free_vectors /= nr_msix_devices; - spin_unlock_irqrestore(&msi_lock, flags); - - if (nvec > free_vectors) { - if (free_vectors > 0) - return free_vectors; - else - return -EBUSY; - } - status = msix_capability_init(dev, entries, nvec); - if (!status && nr_msix_devices > 0) - nr_msix_devices--; - return status; } @@ -1251,53 +854,47 @@ void pci_disable_msix(struct pci_dev* dev) if (!(control & PCI_MSIX_FLAGS_ENABLE)) return; + disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + temp = dev->irq; - if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { - int state, vector, head, tail = 0, warning = 0; + if (!msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { + int irq, head, tail = 0, warning = 0; unsigned long flags; - vector = head = dev->irq; - spin_lock_irqsave(&msi_lock, flags); + irq = head = dev->irq; + dev->irq = temp; /* Restore pin IRQ */ while (head != tail) { - state = msi_desc[vector]->msi_attrib.state; - if (state) + spin_lock_irqsave(&msi_lock, flags); + tail = msi_desc[irq]->link.tail; + spin_unlock_irqrestore(&msi_lock, flags); + if (irq_has_action(irq)) warning = 1; - else { - vector_irq[vector] = 0; /* free it */ - nr_released_vectors++; - } - tail = msi_desc[vector]->link.tail; - vector = tail; + else if (irq != head) /* Release MSI-X irq */ + msi_free_irq(dev, irq); + irq = tail; } - spin_unlock_irqrestore(&msi_lock, flags); + msi_free_irq(dev, irq); if (warning) { - dev->irq = temp; printk(KERN_WARNING "PCI: %s: pci_disable_msix() called without " - "free_irq() on all MSI-X vectors\n", + "free_irq() on all MSI-X irqs\n", pci_name(dev)); BUG_ON(warning > 0); - } else { - dev->irq = temp; - disable_msi_mode(dev, - pci_find_capability(dev, PCI_CAP_ID_MSIX), - PCI_CAP_ID_MSIX); - } } } /** - * msi_remove_pci_irq_vectors - reclaim MSI(X) vectors to unused state + * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state * @dev: pointer to the pci_dev data structure of MSI(X) device function * * Being called during hotplug remove, from which the device function - * is hot-removed. All previous assigned MSI/MSI-X vectors, if + * is hot-removed. All previous assigned MSI/MSI-X irqs, if * allocated for this device function, are reclaimed to unused state, * which may be used later on. **/ void msi_remove_pci_irq_vectors(struct pci_dev* dev) { - int state, pos, temp; + int pos, temp; unsigned long flags; if (!pci_msi_enable || !dev) @@ -1305,42 +902,38 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev) temp = dev->irq; /* Save IOAPIC IRQ */ pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { - spin_lock_irqsave(&msi_lock, flags); - state = msi_desc[dev->irq]->msi_attrib.state; - spin_unlock_irqrestore(&msi_lock, flags); - if (state) { + if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) { + if (irq_has_action(dev->irq)) { printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() " - "called without free_irq() on MSI vector %d\n", + "called without free_irq() on MSI irq %d\n", pci_name(dev), dev->irq); - BUG_ON(state > 0); - } else /* Release MSI vector assigned to this device */ - msi_free_vector(dev, dev->irq, 0); + BUG_ON(irq_has_action(dev->irq)); + } else /* Release MSI irq assigned to this device */ + msi_free_irq(dev, dev->irq); dev->irq = temp; /* Restore IOAPIC IRQ */ } pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { - int vector, head, tail = 0, warning = 0; + if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { + int irq, head, tail = 0, warning = 0; void __iomem *base = NULL; - vector = head = dev->irq; + irq = head = dev->irq; while (head != tail) { spin_lock_irqsave(&msi_lock, flags); - state = msi_desc[vector]->msi_attrib.state; - tail = msi_desc[vector]->link.tail; - base = msi_desc[vector]->mask_base; + tail = msi_desc[irq]->link.tail; + base = msi_desc[irq]->mask_base; spin_unlock_irqrestore(&msi_lock, flags); - if (state) + if (irq_has_action(irq)) warning = 1; - else if (vector != head) /* Release MSI-X vector */ - msi_free_vector(dev, vector, 0); - vector = tail; + else if (irq != head) /* Release MSI-X irq */ + msi_free_irq(dev, irq); + irq = tail; } - msi_free_vector(dev, vector, 0); + msi_free_irq(dev, irq); if (warning) { iounmap(base); printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() " - "called without free_irq() on all MSI-X vectors\n", + "called without free_irq() on all MSI-X irqs\n", pci_name(dev)); BUG_ON(warning > 0); } diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 56951c39d3..f0cca1772f 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -6,84 +6,6 @@ #ifndef MSI_H #define MSI_H -/* - * MSI operation vector. Used by the msi core code (drivers/pci/msi.c) - * to abstract platform-specific tasks relating to MSI address generation - * and resource management. - */ -struct msi_ops { - /** - * setup - generate an MSI bus address and data for a given vector - * @pdev: PCI device context (in) - * @vector: vector allocated by the msi core (in) - * @addr_hi: upper 32 bits of PCI bus MSI address (out) - * @addr_lo: lower 32 bits of PCI bus MSI address (out) - * @data: MSI data payload (out) - * - * Description: The setup op is used to generate a PCI bus addres and - * data which the msi core will program into the card MSI capability - * registers. The setup routine is responsible for picking an initial - * cpu to target the MSI at. The setup routine is responsible for - * examining pdev to determine the MSI capabilities of the card and - * generating a suitable address/data. The setup routine is - * responsible for allocating and tracking any system resources it - * needs to route the MSI to the cpu it picks, and for associating - * those resources with the passed in vector. - * - * Returns 0 if the MSI address/data was successfully setup. - **/ - - int (*setup) (struct pci_dev *pdev, unsigned int vector, - u32 *addr_hi, u32 *addr_lo, u32 *data); - - /** - * teardown - release resources allocated by setup - * @vector: vector context for resources (in) - * - * Description: The teardown op is used to release any resources - * that were allocated in the setup routine associated with the passed - * in vector. - **/ - - void (*teardown) (unsigned int vector); - - /** - * target - retarget an MSI at a different cpu - * @vector: vector context for resources (in) - * @cpu: new cpu to direct vector at (in) - * @addr_hi: new value of PCI bus upper 32 bits (in/out) - * @addr_lo: new value of PCI bus lower 32 bits (in/out) - * - * Description: The target op is used to redirect an MSI vector - * at a different cpu. addr_hi/addr_lo coming in are the existing - * values that the MSI core has programmed into the card. The - * target code is responsible for freeing any resources (if any) - * associated with the old address, and generating a new PCI bus - * addr_hi/addr_lo that will redirect the vector at the indicated cpu. - **/ - - void (*target) (unsigned int vector, unsigned int cpu, - u32 *addr_hi, u32 *addr_lo); -}; - -extern int msi_register(struct msi_ops *ops); - -#include - -/* - * Assume the maximum number of hot plug slots supported by the system is about - * ten. The worstcase is that each of these slots is hot-added with a device, - * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which - * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined - * as below to ensure at least one message is assigned to each detected MSI/ - * MSI-X device function. - */ -#define NR_HP_RESERVED_VECTORS 20 - -extern int vector_irq[NR_VECTORS]; -extern void (*interrupt[NR_IRQS])(void); -extern int pci_vector_resources(int last, int nr_released); - /* * MSI-X Address Register */ @@ -110,8 +32,8 @@ extern int pci_vector_resources(int last, int nr_released); (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) #define multi_msi_enable(control, num) \ control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); -#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) -#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) +#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) +#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) #define msi_enable(control, num) multi_msi_enable(control, num); \ control |= PCI_MSI_FLAGS_ENABLE @@ -125,32 +47,4 @@ extern int pci_vector_resources(int last, int nr_released); #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) -struct msi_desc { - struct { - __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ - __u8 maskbit : 1; /* mask-pending bit supported ? */ - __u8 state : 1; /* {0: free, 1: busy} */ - __u8 reserved: 1; /* reserved */ - __u8 entry_nr; /* specific enabled entry */ - __u8 default_vector; /* default pre-assigned vector */ - __u8 unused; /* formerly unused destination cpu*/ - }msi_attrib; - - struct { - __u16 head; - __u16 tail; - }link; - - void __iomem *mask_base; - struct pci_dev *dev; - -#ifdef CONFIG_PM - /* PM save area for MSIX address/data */ - - u32 address_hi_save; - u32 address_lo_save; - u32 data_save; -#endif -}; - #endif /* MSI_H */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 54404917be..8f7bcf56f1 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -55,16 +55,16 @@ pbus_assign_resources_sorted(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { u16 class = dev->class >> 8; - /* Don't touch classless devices or host bridges. */ + /* Don't touch classless devices or host bridges or ioapics. */ if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) continue; - /* Don't touch ioapics if it has the assigned resources. */ + /* Don't touch ioapic devices already enabled by firmware */ if (class == PCI_CLASS_SYSTEM_PIC) { - res = &dev->resource[0]; - if (res[0].start || res[1].start || res[2].start || - res[3].start || res[4].start || res[5].start) + u16 command; + pci_read_config_word(dev, PCI_COMMAND, &command); + if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) continue; } diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index cc5032b6f4..3f0f7b8fa8 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -141,9 +141,9 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", - "write", dt->tm_sec, dt->tm_min, - dt->tm_hour, dt->tm_mday, - dt->tm_mon, dt->tm_year, dt->tm_wday); + "write", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); *buf++ = 0; /* first register addr */ buf[DS1307_REG_SECS] = BIN2BCD(t->tm_sec); diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 9c68ec99af..67e816a9a3 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -55,7 +55,7 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) } dev_dbg(&client->dev, - "%s: raw read data - counters=%02x,%02x,%02x,%02x\n" + "%s: raw read data - counters=%02x,%02x,%02x,%02x\n", __FUNCTION__, buf[0], buf[1], buf[2], buf[3]); time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; @@ -96,7 +96,7 @@ static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm) unsigned long secs; dev_dbg(&client->dev, - "%s: secs=%d, mins=%d, hours=%d, ", + "%s: secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index bbdad09947..2a86632580 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -91,7 +91,7 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) unsigned char buf[8] = { RS5C372_REG_BASE }; dev_dbg(&client->dev, - "%s: secs=%d, mins=%d, hours=%d ", + "%s: secs=%d, mins=%d, hours=%d " "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); @@ -126,7 +126,7 @@ static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) return -EIO; } - dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, trim); + dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, *trim); if (osc) *osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768; diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index b120896c8a..a433cc78ef 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -1843,7 +1843,7 @@ static int __devinit riva_get_EDID_OF(struct fb_info *info, struct pci_dev *pd) for (i = 0; propnames[i] != NULL; ++i) { pedid = get_property(dp, propnames[i], NULL); if (pedid != NULL) { - par->EDID = pedid; + par->EDID = (unsigned char *)pedid; NVTRACE("LCD found.\n"); return 1; } diff --git a/fs/Kconfig b/fs/Kconfig index 68f4561423..674cfbb83a 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -995,6 +995,18 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. +config ECRYPT_FS + tristate "eCrypt filesystem layer support (EXPERIMENTAL)" + depends on EXPERIMENTAL && KEYS && CRYPTO + help + Encrypted filesystem that operates on the VFS layer. See + to learn more about + eCryptfs. Userspace components are required and can be + obtained from . + + To compile this file system support as a module, choose M here: the + module will be called ecryptfs. + config HFS_FS tristate "Apple Macintosh file system support (EXPERIMENTAL)" depends on BLOCK && EXPERIMENTAL diff --git a/fs/Makefile b/fs/Makefile index 819b2a93be..fd24d67a7c 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ +obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ diff --git a/fs/dcache.c b/fs/dcache.c index fc2faa44f8..2355bddad8 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -291,9 +291,9 @@ struct dentry * dget_locked(struct dentry *dentry) * it can be unhashed only if it has no children, or if it is the root * of a filesystem. * - * If the inode has a DCACHE_DISCONNECTED alias, then prefer + * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer * any other hashed alias over that one unless @want_discon is set, - * in which case only return a DCACHE_DISCONNECTED alias. + * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias. */ static struct dentry * __d_find_alias(struct inode *inode, int want_discon) @@ -309,7 +309,8 @@ static struct dentry * __d_find_alias(struct inode *inode, int want_discon) prefetch(next); alias = list_entry(tmp, struct dentry, d_alias); if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { - if (alias->d_flags & DCACHE_DISCONNECTED) + if (IS_ROOT(alias) && + (alias->d_flags & DCACHE_DISCONNECTED)) discon_alias = alias; else if (!want_discon) { __dget_locked(alias); @@ -1004,7 +1005,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) { struct dentry *new = NULL; - if (inode) { + if (inode && S_ISDIR(inode->i_mode)) { spin_lock(&dcache_lock); new = __d_find_alias(inode, 1); if (new) { diff --git a/fs/ecryptfs/Makefile b/fs/ecryptfs/Makefile new file mode 100644 index 0000000000..ca6562451e --- /dev/null +++ b/fs/ecryptfs/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux 2.6 eCryptfs +# + +obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o + +ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o debug.o diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c new file mode 100644 index 0000000000..ed35a9712f --- /dev/null +++ b/fs/ecryptfs/crypto.c @@ -0,0 +1,1659 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2004 Erez Zadok + * Copyright (C) 2001-2004 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +static int +ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv); +static int +ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv); + +/** + * ecryptfs_to_hex + * @dst: Buffer to take hex character representation of contents of + * src; must be at least of size (src_size * 2) + * @src: Buffer to be converted to a hex string respresentation + * @src_size: number of bytes to convert + */ +void ecryptfs_to_hex(char *dst, char *src, size_t src_size) +{ + int x; + + for (x = 0; x < src_size; x++) + sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]); +} + +/** + * ecryptfs_from_hex + * @dst: Buffer to take the bytes from src hex; must be at least of + * size (src_size / 2) + * @src: Buffer to be converted from a hex string respresentation to raw value + * @dst_size: size of dst buffer, or number of hex characters pairs to convert + */ +void ecryptfs_from_hex(char *dst, char *src, int dst_size) +{ + int x; + char tmp[3] = { 0, }; + + for (x = 0; x < dst_size; x++) { + tmp[0] = src[x * 2]; + tmp[1] = src[x * 2 + 1]; + dst[x] = (unsigned char)simple_strtol(tmp, NULL, 16); + } +} + +/** + * ecryptfs_calculate_md5 - calculates the md5 of @src + * @dst: Pointer to 16 bytes of allocated memory + * @crypt_stat: Pointer to crypt_stat struct for the current inode + * @src: Data to be md5'd + * @len: Length of @src + * + * Uses the allocated crypto context that crypt_stat references to + * generate the MD5 sum of the contents of src. + */ +static int ecryptfs_calculate_md5(char *dst, + struct ecryptfs_crypt_stat *crypt_stat, + char *src, int len) +{ + int rc = 0; + struct scatterlist sg; + + mutex_lock(&crypt_stat->cs_md5_tfm_mutex); + sg_init_one(&sg, (u8 *)src, len); + if (!crypt_stat->md5_tfm) { + crypt_stat->md5_tfm = + crypto_alloc_tfm("md5", CRYPTO_TFM_REQ_MAY_SLEEP); + if (!crypt_stat->md5_tfm) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error attempting to " + "allocate crypto context\n"); + goto out; + } + } + crypto_digest_init(crypt_stat->md5_tfm); + crypto_digest_update(crypt_stat->md5_tfm, &sg, 1); + crypto_digest_final(crypt_stat->md5_tfm, dst); + mutex_unlock(&crypt_stat->cs_md5_tfm_mutex); +out: + return rc; +} + +/** + * ecryptfs_derive_iv + * @iv: destination for the derived iv vale + * @crypt_stat: Pointer to crypt_stat struct for the current inode + * @offset: Offset of the page whose's iv we are to derive + * + * Generate the initialization vector from the given root IV and page + * offset. + * + * Returns zero on success; non-zero on error. + */ +static int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, + pgoff_t offset) +{ + int rc = 0; + char dst[MD5_DIGEST_SIZE]; + char src[ECRYPTFS_MAX_IV_BYTES + 16]; + + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "root iv:\n"); + ecryptfs_dump_hex(crypt_stat->root_iv, crypt_stat->iv_bytes); + } + /* TODO: It is probably secure to just cast the least + * significant bits of the root IV into an unsigned long and + * add the offset to that rather than go through all this + * hashing business. -Halcrow */ + memcpy(src, crypt_stat->root_iv, crypt_stat->iv_bytes); + memset((src + crypt_stat->iv_bytes), 0, 16); + snprintf((src + crypt_stat->iv_bytes), 16, "%ld", offset); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "source:\n"); + ecryptfs_dump_hex(src, (crypt_stat->iv_bytes + 16)); + } + rc = ecryptfs_calculate_md5(dst, crypt_stat, src, + (crypt_stat->iv_bytes + 16)); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error attempting to compute " + "MD5 while generating IV for a page\n"); + goto out; + } + memcpy(iv, dst, crypt_stat->iv_bytes); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "derived iv:\n"); + ecryptfs_dump_hex(iv, crypt_stat->iv_bytes); + } +out: + return rc; +} + +/** + * ecryptfs_init_crypt_stat + * @crypt_stat: Pointer to the crypt_stat struct to initialize. + * + * Initialize the crypt_stat structure. + */ +void +ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) +{ + memset((void *)crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat)); + mutex_init(&crypt_stat->cs_mutex); + mutex_init(&crypt_stat->cs_tfm_mutex); + mutex_init(&crypt_stat->cs_md5_tfm_mutex); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_STRUCT_INITIALIZED); +} + +/** + * ecryptfs_destruct_crypt_stat + * @crypt_stat: Pointer to the crypt_stat struct to initialize. + * + * Releases all memory associated with a crypt_stat struct. + */ +void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) +{ + if (crypt_stat->tfm) + crypto_free_tfm(crypt_stat->tfm); + if (crypt_stat->md5_tfm) + crypto_free_tfm(crypt_stat->md5_tfm); + memset(crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat)); +} + +void ecryptfs_destruct_mount_crypt_stat( + struct ecryptfs_mount_crypt_stat *mount_crypt_stat) +{ + if (mount_crypt_stat->global_auth_tok_key) + key_put(mount_crypt_stat->global_auth_tok_key); + if (mount_crypt_stat->global_key_tfm) + crypto_free_tfm(mount_crypt_stat->global_key_tfm); + memset(mount_crypt_stat, 0, sizeof(struct ecryptfs_mount_crypt_stat)); +} + +/** + * virt_to_scatterlist + * @addr: Virtual address + * @size: Size of data; should be an even multiple of the block size + * @sg: Pointer to scatterlist array; set to NULL to obtain only + * the number of scatterlist structs required in array + * @sg_size: Max array size + * + * Fills in a scatterlist array with page references for a passed + * virtual address. + * + * Returns the number of scatterlist structs in array used + */ +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, + int sg_size) +{ + int i = 0; + struct page *pg; + int offset; + int remainder_of_page; + + while (size > 0 && i < sg_size) { + pg = virt_to_page(addr); + offset = offset_in_page(addr); + if (sg) { + sg[i].page = pg; + sg[i].offset = offset; + } + remainder_of_page = PAGE_CACHE_SIZE - offset; + if (size >= remainder_of_page) { + if (sg) + sg[i].length = remainder_of_page; + addr += remainder_of_page; + size -= remainder_of_page; + } else { + if (sg) + sg[i].length = size; + addr += size; + size = 0; + } + i++; + } + if (size > 0) + return -ENOMEM; + return i; +} + +/** + * encrypt_scatterlist + * @crypt_stat: Pointer to the crypt_stat struct to initialize. + * @dest_sg: Destination of encrypted data + * @src_sg: Data to be encrypted + * @size: Length of data to be encrypted + * @iv: iv to use during encryption + * + * Returns the number of bytes encrypted; negative value on error + */ +static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv) +{ + int rc = 0; + + BUG_ON(!crypt_stat || !crypt_stat->tfm + || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ECRYPTFS_STRUCT_INITIALIZED)); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n", + crypt_stat->key_size); + ecryptfs_dump_hex(crypt_stat->key, + crypt_stat->key_size); + } + /* Consider doing this once, when the file is opened */ + mutex_lock(&crypt_stat->cs_tfm_mutex); + rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key, + crypt_stat->key_size); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size); + crypto_cipher_encrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, iv); + mutex_unlock(&crypt_stat->cs_tfm_mutex); +out: + return rc; +} + +static void +ecryptfs_extent_to_lwr_pg_idx_and_offset(unsigned long *lower_page_idx, + int *byte_offset, + struct ecryptfs_crypt_stat *crypt_stat, + unsigned long extent_num) +{ + unsigned long lower_extent_num; + int extents_occupied_by_headers_at_front; + int bytes_occupied_by_headers_at_front; + int extent_offset; + int extents_per_page; + + bytes_occupied_by_headers_at_front = + ( crypt_stat->header_extent_size + * crypt_stat->num_header_extents_at_front ); + extents_occupied_by_headers_at_front = + ( bytes_occupied_by_headers_at_front + / crypt_stat->extent_size ); + lower_extent_num = extents_occupied_by_headers_at_front + extent_num; + extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; + (*lower_page_idx) = lower_extent_num / extents_per_page; + extent_offset = lower_extent_num % extents_per_page; + (*byte_offset) = extent_offset * crypt_stat->extent_size; + ecryptfs_printk(KERN_DEBUG, " * crypt_stat->header_extent_size = " + "[%d]\n", crypt_stat->header_extent_size); + ecryptfs_printk(KERN_DEBUG, " * crypt_stat->" + "num_header_extents_at_front = [%d]\n", + crypt_stat->num_header_extents_at_front); + ecryptfs_printk(KERN_DEBUG, " * extents_occupied_by_headers_at_" + "front = [%d]\n", extents_occupied_by_headers_at_front); + ecryptfs_printk(KERN_DEBUG, " * lower_extent_num = [0x%.16x]\n", + lower_extent_num); + ecryptfs_printk(KERN_DEBUG, " * extents_per_page = [%d]\n", + extents_per_page); + ecryptfs_printk(KERN_DEBUG, " * (*lower_page_idx) = [0x%.16x]\n", + (*lower_page_idx)); + ecryptfs_printk(KERN_DEBUG, " * extent_offset = [%d]\n", + extent_offset); + ecryptfs_printk(KERN_DEBUG, " * (*byte_offset) = [%d]\n", + (*byte_offset)); +} + +static int ecryptfs_write_out_page(struct ecryptfs_page_crypt_context *ctx, + struct page *lower_page, + struct inode *lower_inode, + int byte_offset_in_page, int bytes_to_write) +{ + int rc = 0; + + if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) { + rc = ecryptfs_commit_lower_page(lower_page, lower_inode, + ctx->param.lower_file, + byte_offset_in_page, + bytes_to_write); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error calling lower " + "commit; rc = [%d]\n", rc); + goto out; + } + } else { + rc = ecryptfs_writepage_and_release_lower_page(lower_page, + lower_inode, + ctx->param.wbc); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error calling lower " + "writepage(); rc = [%d]\n", rc); + goto out; + } + } +out: + return rc; +} + +static int ecryptfs_read_in_page(struct ecryptfs_page_crypt_context *ctx, + struct page **lower_page, + struct inode *lower_inode, + unsigned long lower_page_idx, + int byte_offset_in_page) +{ + int rc = 0; + + if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) { + /* TODO: Limit this to only the data extents that are + * needed */ + rc = ecryptfs_get_lower_page(lower_page, lower_inode, + ctx->param.lower_file, + lower_page_idx, + byte_offset_in_page, + (PAGE_CACHE_SIZE + - byte_offset_in_page)); + if (rc) { + ecryptfs_printk( + KERN_ERR, "Error attempting to grab, map, " + "and prepare_write lower page with index " + "[0x%.16x]; rc = [%d]\n", lower_page_idx, rc); + goto out; + } + } else { + rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, + lower_inode, + lower_page_idx); + if (rc) { + ecryptfs_printk( + KERN_ERR, "Error attempting to grab and map " + "lower page with index [0x%.16x]; rc = [%d]\n", + lower_page_idx, rc); + goto out; + } + } +out: + return rc; +} + +/** + * ecryptfs_encrypt_page + * @ctx: The context of the page + * + * Encrypt an eCryptfs page. This is done on a per-extent basis. Note + * that eCryptfs pages may straddle the lower pages -- for instance, + * if the file was created on a machine with an 8K page size + * (resulting in an 8K header), and then the file is copied onto a + * host with a 32K page size, then when reading page 0 of the eCryptfs + * file, 24K of page 0 of the lower file will be read and decrypted, + * and then 8K of page 1 of the lower file will be read and decrypted. + * + * The actual operations performed on each page depends on the + * contents of the ecryptfs_page_crypt_context struct. + * + * Returns zero on success; negative on error + */ +int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx) +{ + char extent_iv[ECRYPTFS_MAX_IV_BYTES]; + unsigned long base_extent; + unsigned long extent_offset = 0; + unsigned long lower_page_idx = 0; + unsigned long prior_lower_page_idx = 0; + struct page *lower_page; + struct inode *lower_inode; + struct ecryptfs_inode_info *inode_info; + struct ecryptfs_crypt_stat *crypt_stat; + int rc = 0; + int lower_byte_offset = 0; + int orig_byte_offset = 0; + int num_extents_per_page; +#define ECRYPTFS_PAGE_STATE_UNREAD 0 +#define ECRYPTFS_PAGE_STATE_READ 1 +#define ECRYPTFS_PAGE_STATE_MODIFIED 2 +#define ECRYPTFS_PAGE_STATE_WRITTEN 3 + int page_state; + + lower_inode = ecryptfs_inode_to_lower(ctx->page->mapping->host); + inode_info = ecryptfs_inode_to_private(ctx->page->mapping->host); + crypt_stat = &inode_info->crypt_stat; + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) { + rc = ecryptfs_copy_page_to_lower(ctx->page, lower_inode, + ctx->param.lower_file); + if (rc) + ecryptfs_printk(KERN_ERR, "Error attempting to copy " + "page at index [0x%.16x]\n", + ctx->page->index); + goto out; + } + num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; + base_extent = (ctx->page->index * num_extents_per_page); + page_state = ECRYPTFS_PAGE_STATE_UNREAD; + while (extent_offset < num_extents_per_page) { + ecryptfs_extent_to_lwr_pg_idx_and_offset( + &lower_page_idx, &lower_byte_offset, crypt_stat, + (base_extent + extent_offset)); + if (prior_lower_page_idx != lower_page_idx + && page_state == ECRYPTFS_PAGE_STATE_MODIFIED) { + rc = ecryptfs_write_out_page(ctx, lower_page, + lower_inode, + orig_byte_offset, + (PAGE_CACHE_SIZE + - orig_byte_offset)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting " + "to write out page; rc = [%d]" + "\n", rc); + goto out; + } + page_state = ECRYPTFS_PAGE_STATE_WRITTEN; + } + if (page_state == ECRYPTFS_PAGE_STATE_UNREAD + || page_state == ECRYPTFS_PAGE_STATE_WRITTEN) { + rc = ecryptfs_read_in_page(ctx, &lower_page, + lower_inode, lower_page_idx, + lower_byte_offset); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting " + "to read in lower page with " + "index [0x%.16x]; rc = [%d]\n", + lower_page_idx, rc); + goto out; + } + orig_byte_offset = lower_byte_offset; + prior_lower_page_idx = lower_page_idx; + page_state = ECRYPTFS_PAGE_STATE_READ; + } + BUG_ON(!(page_state == ECRYPTFS_PAGE_STATE_MODIFIED + || page_state == ECRYPTFS_PAGE_STATE_READ)); + rc = ecryptfs_derive_iv(extent_iv, crypt_stat, + (base_extent + extent_offset)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to " + "derive IV for extent [0x%.16x]; " + "rc = [%d]\n", + (base_extent + extent_offset), rc); + goto out; + } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Encrypting extent " + "with iv:\n"); + ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " + "encryption:\n"); + ecryptfs_dump_hex((char *) + (page_address(ctx->page) + + (extent_offset + * crypt_stat->extent_size)), 8); + } + rc = ecryptfs_encrypt_page_offset( + crypt_stat, lower_page, lower_byte_offset, ctx->page, + (extent_offset * crypt_stat->extent_size), + crypt_stat->extent_size, extent_iv); + ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16x]; " + "rc = [%d]\n", + (base_extent + extent_offset), rc); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " + "encryption:\n"); + ecryptfs_dump_hex((char *)(page_address(lower_page) + + lower_byte_offset), 8); + } + page_state = ECRYPTFS_PAGE_STATE_MODIFIED; + extent_offset++; + } + BUG_ON(orig_byte_offset != 0); + rc = ecryptfs_write_out_page(ctx, lower_page, lower_inode, 0, + (lower_byte_offset + + crypt_stat->extent_size)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to write out " + "page; rc = [%d]\n", rc); + goto out; + } +out: + return rc; +} + +/** + * ecryptfs_decrypt_page + * @file: The ecryptfs file + * @page: The page in ecryptfs to decrypt + * + * Decrypt an eCryptfs page. This is done on a per-extent basis. Note + * that eCryptfs pages may straddle the lower pages -- for instance, + * if the file was created on a machine with an 8K page size + * (resulting in an 8K header), and then the file is copied onto a + * host with a 32K page size, then when reading page 0 of the eCryptfs + * file, 24K of page 0 of the lower file will be read and decrypted, + * and then 8K of page 1 of the lower file will be read and decrypted. + * + * Returns zero on success; negative on error + */ +int ecryptfs_decrypt_page(struct file *file, struct page *page) +{ + char extent_iv[ECRYPTFS_MAX_IV_BYTES]; + unsigned long base_extent; + unsigned long extent_offset = 0; + unsigned long lower_page_idx = 0; + unsigned long prior_lower_page_idx = 0; + struct page *lower_page; + char *lower_page_virt = NULL; + struct inode *lower_inode; + struct ecryptfs_crypt_stat *crypt_stat; + int rc = 0; + int byte_offset; + int num_extents_per_page; + int page_state; + + crypt_stat = &(ecryptfs_inode_to_private( + page->mapping->host)->crypt_stat); + lower_inode = ecryptfs_inode_to_lower(page->mapping->host); + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) { + rc = ecryptfs_do_readpage(file, page, page->index); + if (rc) + ecryptfs_printk(KERN_ERR, "Error attempting to copy " + "page at index [0x%.16x]\n", + page->index); + goto out; + } + num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; + base_extent = (page->index * num_extents_per_page); + lower_page_virt = kmem_cache_alloc(ecryptfs_lower_page_cache, + SLAB_KERNEL); + if (!lower_page_virt) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error getting page for encrypted " + "lower page(s)\n"); + goto out; + } + lower_page = virt_to_page(lower_page_virt); + page_state = ECRYPTFS_PAGE_STATE_UNREAD; + while (extent_offset < num_extents_per_page) { + ecryptfs_extent_to_lwr_pg_idx_and_offset( + &lower_page_idx, &byte_offset, crypt_stat, + (base_extent + extent_offset)); + if (prior_lower_page_idx != lower_page_idx + || page_state == ECRYPTFS_PAGE_STATE_UNREAD) { + rc = ecryptfs_do_readpage(file, lower_page, + lower_page_idx); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error reading " + "lower encrypted page; rc = " + "[%d]\n", rc); + goto out; + } + prior_lower_page_idx = lower_page_idx; + page_state = ECRYPTFS_PAGE_STATE_READ; + } + rc = ecryptfs_derive_iv(extent_iv, crypt_stat, + (base_extent + extent_offset)); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to " + "derive IV for extent [0x%.16x]; rc = " + "[%d]\n", + (base_extent + extent_offset), rc); + goto out; + } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Decrypting extent " + "with iv:\n"); + ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); + ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " + "decryption:\n"); + ecryptfs_dump_hex((lower_page_virt + byte_offset), 8); + } + rc = ecryptfs_decrypt_page_offset(crypt_stat, page, + (extent_offset + * crypt_stat->extent_size), + lower_page, byte_offset, + crypt_stat->extent_size, + extent_iv); + if (rc != crypt_stat->extent_size) { + ecryptfs_printk(KERN_ERR, "Error attempting to " + "decrypt extent [0x%.16x]\n", + (base_extent + extent_offset)); + goto out; + } + rc = 0; + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " + "decryption:\n"); + ecryptfs_dump_hex((char *)(page_address(page) + + byte_offset), 8); + } + extent_offset++; + } +out: + if (lower_page_virt) + kmem_cache_free(ecryptfs_lower_page_cache, lower_page_virt); + return rc; +} + +/** + * decrypt_scatterlist + * + * Returns the number of bytes decrypted; negative value on error + */ +static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv) +{ + int rc = 0; + + /* Consider doing this once, when the file is opened */ + mutex_lock(&crypt_stat->cs_tfm_mutex); + rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key, + crypt_stat->key_size); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size); + rc = crypto_cipher_decrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, + iv); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n", + rc); + goto out; + } + rc = size; +out: + return rc; +} + +/** + * ecryptfs_encrypt_page_offset + * + * Returns the number of bytes encrypted + */ +static int +ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv) +{ + struct scatterlist src_sg, dst_sg; + + src_sg.page = src_page; + src_sg.offset = src_offset; + src_sg.length = size; + dst_sg.page = dst_page; + dst_sg.offset = dst_offset; + dst_sg.length = size; + return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); +} + +/** + * ecryptfs_decrypt_page_offset + * + * Returns the number of bytes decrypted + */ +static int +ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, int dst_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv) +{ + struct scatterlist src_sg, dst_sg; + + src_sg.page = src_page; + src_sg.offset = src_offset; + src_sg.length = size; + dst_sg.page = dst_page; + dst_sg.offset = dst_offset; + dst_sg.length = size; + return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); +} + +#define ECRYPTFS_MAX_SCATTERLIST_LEN 4 + +/** + * ecryptfs_init_crypt_ctx + * @crypt_stat: Uninitilized crypt stats structure + * + * Initialize the crypto context. + * + * TODO: Performance: Keep a cache of initialized cipher contexts; + * only init if needed + */ +int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) +{ + int rc = -EINVAL; + + if (!crypt_stat->cipher) { + ecryptfs_printk(KERN_ERR, "No cipher specified\n"); + goto out; + } + ecryptfs_printk(KERN_DEBUG, + "Initializing cipher [%s]; strlen = [%d]; " + "key_size_bits = [%d]\n", + crypt_stat->cipher, (int)strlen(crypt_stat->cipher), + crypt_stat->key_size << 3); + if (crypt_stat->tfm) { + rc = 0; + goto out; + } + mutex_lock(&crypt_stat->cs_tfm_mutex); + crypt_stat->tfm = crypto_alloc_tfm(crypt_stat->cipher, + ECRYPTFS_DEFAULT_CHAINING_MODE + | CRYPTO_TFM_REQ_WEAK_KEY); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + if (!crypt_stat->tfm) { + ecryptfs_printk(KERN_ERR, "cryptfs: init_crypt_ctx(): " + "Error initializing cipher [%s]\n", + crypt_stat->cipher); + goto out; + } + rc = 0; +out: + return rc; +} + +static void set_extent_mask_and_shift(struct ecryptfs_crypt_stat *crypt_stat) +{ + int extent_size_tmp; + + crypt_stat->extent_mask = 0xFFFFFFFF; + crypt_stat->extent_shift = 0; + if (crypt_stat->extent_size == 0) + return; + extent_size_tmp = crypt_stat->extent_size; + while ((extent_size_tmp & 0x01) == 0) { + extent_size_tmp >>= 1; + crypt_stat->extent_mask <<= 1; + crypt_stat->extent_shift++; + } +} + +void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat) +{ + /* Default values; may be overwritten as we are parsing the + * packets. */ + crypt_stat->extent_size = ECRYPTFS_DEFAULT_EXTENT_SIZE; + set_extent_mask_and_shift(crypt_stat); + crypt_stat->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES; + if (PAGE_CACHE_SIZE <= ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { + crypt_stat->header_extent_size = + ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE; + } else + crypt_stat->header_extent_size = PAGE_CACHE_SIZE; + crypt_stat->num_header_extents_at_front = 1; +} + +/** + * ecryptfs_compute_root_iv + * @crypt_stats + * + * On error, sets the root IV to all 0's. + */ +int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat) +{ + int rc = 0; + char dst[MD5_DIGEST_SIZE]; + + BUG_ON(crypt_stat->iv_bytes > MD5_DIGEST_SIZE); + BUG_ON(crypt_stat->iv_bytes <= 0); + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID)) { + rc = -EINVAL; + ecryptfs_printk(KERN_WARNING, "Session key not valid; " + "cannot generate root IV\n"); + goto out; + } + rc = ecryptfs_calculate_md5(dst, crypt_stat, crypt_stat->key, + crypt_stat->key_size); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error attempting to compute " + "MD5 while generating root IV\n"); + goto out; + } + memcpy(crypt_stat->root_iv, dst, crypt_stat->iv_bytes); +out: + if (rc) { + memset(crypt_stat->root_iv, 0, crypt_stat->iv_bytes); + ECRYPTFS_SET_FLAG(crypt_stat->flags, + ECRYPTFS_SECURITY_WARNING); + } + return rc; +} + +static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat) +{ + get_random_bytes(crypt_stat->key, crypt_stat->key_size); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); + ecryptfs_compute_root_iv(crypt_stat); + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n"); + ecryptfs_dump_hex(crypt_stat->key, + crypt_stat->key_size); + } +} + +/** + * ecryptfs_set_default_crypt_stat_vals + * @crypt_stat + * + * Default values in the event that policy does not override them. + */ +static void ecryptfs_set_default_crypt_stat_vals( + struct ecryptfs_crypt_stat *crypt_stat, + struct ecryptfs_mount_crypt_stat *mount_crypt_stat) +{ + ecryptfs_set_default_sizes(crypt_stat); + strcpy(crypt_stat->cipher, ECRYPTFS_DEFAULT_CIPHER); + crypt_stat->key_size = ECRYPTFS_DEFAULT_KEY_BYTES; + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); + crypt_stat->file_version = ECRYPTFS_FILE_VERSION; + crypt_stat->mount_crypt_stat = mount_crypt_stat; +} + +/** + * ecryptfs_new_file_context + * @ecryptfs_dentry + * + * If the crypto context for the file has not yet been established, + * this is where we do that. Establishing a new crypto context + * involves the following decisions: + * - What cipher to use? + * - What set of authentication tokens to use? + * Here we just worry about getting enough information into the + * authentication tokens so that we know that they are available. + * We associate the available authentication tokens with the new file + * via the set of signatures in the crypt_stat struct. Later, when + * the headers are actually written out, we may again defer to + * userspace to perform the encryption of the session key; for the + * foreseeable future, this will be the case with public key packets. + * + * Returns zero on success; non-zero otherwise + */ +/* Associate an authentication token(s) with the file */ +int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) +{ + int rc = 0; + struct ecryptfs_crypt_stat *crypt_stat = + &ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat = + &ecryptfs_superblock_to_private( + ecryptfs_dentry->d_sb)->mount_crypt_stat; + int cipher_name_len; + + ecryptfs_set_default_crypt_stat_vals(crypt_stat, mount_crypt_stat); + /* See if there are mount crypt options */ + if (mount_crypt_stat->global_auth_tok) { + ecryptfs_printk(KERN_DEBUG, "Initializing context for new " + "file using mount_crypt_stat\n"); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); + memcpy(crypt_stat->keysigs[crypt_stat->num_keysigs++], + mount_crypt_stat->global_auth_tok_sig, + ECRYPTFS_SIG_SIZE_HEX); + cipher_name_len = + strlen(mount_crypt_stat->global_default_cipher_name); + memcpy(crypt_stat->cipher, + mount_crypt_stat->global_default_cipher_name, + cipher_name_len); + crypt_stat->cipher[cipher_name_len] = '\0'; + crypt_stat->key_size = + mount_crypt_stat->global_default_cipher_key_size; + ecryptfs_generate_new_key(crypt_stat); + } else + /* We should not encounter this scenario since we + * should detect lack of global_auth_tok at mount time + * TODO: Applies to 0.1 release only; remove in future + * release */ + BUG(); + rc = ecryptfs_init_crypt_ctx(crypt_stat); + if (rc) + ecryptfs_printk(KERN_ERR, "Error initializing cryptographic " + "context for cipher [%s]: rc = [%d]\n", + crypt_stat->cipher, rc); + return rc; +} + +/** + * contains_ecryptfs_marker - check for the ecryptfs marker + * @data: The data block in which to check + * + * Returns one if marker found; zero if not found + */ +int contains_ecryptfs_marker(char *data) +{ + u32 m_1, m_2; + + memcpy(&m_1, data, 4); + m_1 = be32_to_cpu(m_1); + memcpy(&m_2, (data + 4), 4); + m_2 = be32_to_cpu(m_2); + if ((m_1 ^ MAGIC_ECRYPTFS_MARKER) == m_2) + return 1; + ecryptfs_printk(KERN_DEBUG, "m_1 = [0x%.8x]; m_2 = [0x%.8x]; " + "MAGIC_ECRYPTFS_MARKER = [0x%.8x]\n", m_1, m_2, + MAGIC_ECRYPTFS_MARKER); + ecryptfs_printk(KERN_DEBUG, "(m_1 ^ MAGIC_ECRYPTFS_MARKER) = " + "[0x%.8x]\n", (m_1 ^ MAGIC_ECRYPTFS_MARKER)); + return 0; +} + +struct ecryptfs_flag_map_elem { + u32 file_flag; + u32 local_flag; +}; + +/* Add support for additional flags by adding elements here. */ +static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = { + {0x00000001, ECRYPTFS_ENABLE_HMAC}, + {0x00000002, ECRYPTFS_ENCRYPTED} +}; + +/** + * ecryptfs_process_flags + * @crypt_stat + * @page_virt: Source data to be parsed + * @bytes_read: Updated with the number of bytes read + * + * Returns zero on success; non-zero if the flag set is invalid + */ +static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat, + char *page_virt, int *bytes_read) +{ + int rc = 0; + int i; + u32 flags; + + memcpy(&flags, page_virt, 4); + flags = be32_to_cpu(flags); + for (i = 0; i < ((sizeof(ecryptfs_flag_map) + / sizeof(struct ecryptfs_flag_map_elem))); i++) + if (flags & ecryptfs_flag_map[i].file_flag) { + ECRYPTFS_SET_FLAG(crypt_stat->flags, + ecryptfs_flag_map[i].local_flag); + } else + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, + ecryptfs_flag_map[i].local_flag); + /* Version is in top 8 bits of the 32-bit flag vector */ + crypt_stat->file_version = ((flags >> 24) & 0xFF); + (*bytes_read) = 4; + return rc; +} + +/** + * write_ecryptfs_marker + * @page_virt: The pointer to in a page to begin writing the marker + * @written: Number of bytes written + * + * Marker = 0x3c81b7f5 + */ +static void write_ecryptfs_marker(char *page_virt, size_t *written) +{ + u32 m_1, m_2; + + get_random_bytes(&m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); + m_2 = (m_1 ^ MAGIC_ECRYPTFS_MARKER); + m_1 = cpu_to_be32(m_1); + memcpy(page_virt, &m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); + m_2 = cpu_to_be32(m_2); + memcpy(page_virt + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &m_2, + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); + (*written) = MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; +} + +static void +write_ecryptfs_flags(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat, + size_t *written) +{ + u32 flags = 0; + int i; + + for (i = 0; i < ((sizeof(ecryptfs_flag_map) + / sizeof(struct ecryptfs_flag_map_elem))); i++) + if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ecryptfs_flag_map[i].local_flag)) + flags |= ecryptfs_flag_map[i].file_flag; + /* Version is in top 8 bits of the 32-bit flag vector */ + flags |= ((((u8)crypt_stat->file_version) << 24) & 0xFF000000); + flags = cpu_to_be32(flags); + memcpy(page_virt, &flags, 4); + (*written) = 4; +} + +struct ecryptfs_cipher_code_str_map_elem { + char cipher_str[16]; + u16 cipher_code; +}; + +/* Add support for additional ciphers by adding elements here. The + * cipher_code is whatever OpenPGP applicatoins use to identify the + * ciphers. List in order of probability. */ +static struct ecryptfs_cipher_code_str_map_elem +ecryptfs_cipher_code_str_map[] = { + {"aes",RFC2440_CIPHER_AES_128 }, + {"blowfish", RFC2440_CIPHER_BLOWFISH}, + {"des3_ede", RFC2440_CIPHER_DES3_EDE}, + {"cast5", RFC2440_CIPHER_CAST_5}, + {"twofish", RFC2440_CIPHER_TWOFISH}, + {"cast6", RFC2440_CIPHER_CAST_6}, + {"aes", RFC2440_CIPHER_AES_192}, + {"aes", RFC2440_CIPHER_AES_256} +}; + +/** + * ecryptfs_code_for_cipher_string + * @str: The string representing the cipher name + * + * Returns zero on no match, or the cipher code on match + */ +u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat) +{ + int i; + u16 code = 0; + struct ecryptfs_cipher_code_str_map_elem *map = + ecryptfs_cipher_code_str_map; + + if (strcmp(crypt_stat->cipher, "aes") == 0) { + switch (crypt_stat->key_size) { + case 16: + code = RFC2440_CIPHER_AES_128; + break; + case 24: + code = RFC2440_CIPHER_AES_192; + break; + case 32: + code = RFC2440_CIPHER_AES_256; + } + } else { + for (i = 0; i < ARRAY_SIZE(ecryptfs_cipher_code_str_map); i++) + if (strcmp(crypt_stat->cipher, map[i].cipher_str) == 0){ + code = map[i].cipher_code; + break; + } + } + return code; +} + +/** + * ecryptfs_cipher_code_to_string + * @str: Destination to write out the cipher name + * @cipher_code: The code to convert to cipher name string + * + * Returns zero on success + */ +int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code) +{ + int rc = 0; + int i; + + str[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(ecryptfs_cipher_code_str_map); i++) + if (cipher_code == ecryptfs_cipher_code_str_map[i].cipher_code) + strcpy(str, ecryptfs_cipher_code_str_map[i].cipher_str); + if (str[0] == '\0') { + ecryptfs_printk(KERN_WARNING, "Cipher code not recognized: " + "[%d]\n", cipher_code); + rc = -EINVAL; + } + return rc; +} + +/** + * ecryptfs_read_header_region + * @data + * @dentry + * @nd + * + * Returns zero on success; non-zero otherwise + */ +int ecryptfs_read_header_region(char *data, struct dentry *dentry, + struct vfsmount *mnt) +{ + struct file *file; + mm_segment_t oldfs; + int rc; + + mnt = mntget(mnt); + file = dentry_open(dentry, mnt, O_RDONLY); + if (IS_ERR(file)) { + ecryptfs_printk(KERN_DEBUG, "Error opening file to " + "read header region\n"); + mntput(mnt); + rc = PTR_ERR(file); + goto out; + } + file->f_pos = 0; + oldfs = get_fs(); + set_fs(get_ds()); + /* For releases 0.1 and 0.2, all of the header information + * fits in the first data extent-sized region. */ + rc = file->f_op->read(file, (char __user *)data, + ECRYPTFS_DEFAULT_EXTENT_SIZE, &file->f_pos); + set_fs(oldfs); + fput(file); + rc = 0; +out: + return rc; +} + +static void +write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat, + size_t *written) +{ + u32 header_extent_size; + u16 num_header_extents_at_front; + + header_extent_size = (u32)crypt_stat->header_extent_size; + num_header_extents_at_front = + (u16)crypt_stat->num_header_extents_at_front; + header_extent_size = cpu_to_be32(header_extent_size); + memcpy(virt, &header_extent_size, 4); + virt += 4; + num_header_extents_at_front = cpu_to_be16(num_header_extents_at_front); + memcpy(virt, &num_header_extents_at_front, 2); + (*written) = 6; +} + +struct kmem_cache *ecryptfs_header_cache_0; +struct kmem_cache *ecryptfs_header_cache_1; +struct kmem_cache *ecryptfs_header_cache_2; + +/** + * ecryptfs_write_headers_virt + * @page_virt + * @crypt_stat + * @ecryptfs_dentry + * + * Format version: 1 + * + * Header Extent: + * Octets 0-7: Unencrypted file size (big-endian) + * Octets 8-15: eCryptfs special marker + * Octets 16-19: Flags + * Octet 16: File format version number (between 0 and 255) + * Octets 17-18: Reserved + * Octet 19: Bit 1 (lsb): Reserved + * Bit 2: Encrypted? + * Bits 3-8: Reserved + * Octets 20-23: Header extent size (big-endian) + * Octets 24-25: Number of header extents at front of file + * (big-endian) + * Octet 26: Begin RFC 2440 authentication token packet set + * Data Extent 0: + * Lower data (CBC encrypted) + * Data Extent 1: + * Lower data (CBC encrypted) + * ... + * + * Returns zero on success + */ +int ecryptfs_write_headers_virt(char *page_virt, + struct ecryptfs_crypt_stat *crypt_stat, + struct dentry *ecryptfs_dentry) +{ + int rc; + size_t written; + size_t offset; + + offset = ECRYPTFS_FILE_SIZE_BYTES; + write_ecryptfs_marker((page_virt + offset), &written); + offset += written; + write_ecryptfs_flags((page_virt + offset), crypt_stat, &written); + offset += written; + write_header_metadata((page_virt + offset), crypt_stat, &written); + offset += written; + rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat, + ecryptfs_dentry, &written, + PAGE_CACHE_SIZE - offset); + if (rc) + ecryptfs_printk(KERN_WARNING, "Error generating key packet " + "set; rc = [%d]\n", rc); + return rc; +} + +/** + * ecryptfs_write_headers + * @lower_file: The lower file struct, which was returned from dentry_open + * + * Write the file headers out. This will likely involve a userspace + * callout, in which the session key is encrypted with one or more + * public keys and/or the passphrase necessary to do the encryption is + * retrieved via a prompt. Exactly what happens at this point should + * be policy-dependent. + * + * Returns zero on success; non-zero on error + */ +int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file) +{ + mm_segment_t oldfs; + struct ecryptfs_crypt_stat *crypt_stat; + char *page_virt; + int current_header_page; + int header_pages; + int rc = 0; + + crypt_stat = &ecryptfs_inode_to_private( + ecryptfs_dentry->d_inode)->crypt_stat; + if (likely(ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ECRYPTFS_ENCRYPTED))) { + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ECRYPTFS_KEY_VALID)) { + ecryptfs_printk(KERN_DEBUG, "Key is " + "invalid; bailing out\n"); + rc = -EINVAL; + goto out; + } + } else { + rc = -EINVAL; + ecryptfs_printk(KERN_WARNING, + "Called with crypt_stat->encrypted == 0\n"); + goto out; + } + /* Released in this function */ + page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER); + if (!page_virt) { + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto out; + } + memset(page_virt, 0, PAGE_CACHE_SIZE); + rc = ecryptfs_write_headers_virt(page_virt, crypt_stat, + ecryptfs_dentry); + if (unlikely(rc)) { + ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n"); + memset(page_virt, 0, PAGE_CACHE_SIZE); + goto out_free; + } + ecryptfs_printk(KERN_DEBUG, + "Writing key packet set to underlying file\n"); + lower_file->f_pos = 0; + oldfs = get_fs(); + set_fs(get_ds()); + ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->" + "write() w/ header page; lower_file->f_pos = " + "[0x%.16x]\n", lower_file->f_pos); + lower_file->f_op->write(lower_file, (char __user *)page_virt, + PAGE_CACHE_SIZE, &lower_file->f_pos); + header_pages = ((crypt_stat->header_extent_size + * crypt_stat->num_header_extents_at_front) + / PAGE_CACHE_SIZE); + memset(page_virt, 0, PAGE_CACHE_SIZE); + current_header_page = 1; + while (current_header_page < header_pages) { + ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->" + "write() w/ zero'd page; lower_file->f_pos = " + "[0x%.16x]\n", lower_file->f_pos); + lower_file->f_op->write(lower_file, (char __user *)page_virt, + PAGE_CACHE_SIZE, &lower_file->f_pos); + current_header_page++; + } + set_fs(oldfs); + ecryptfs_printk(KERN_DEBUG, + "Done writing key packet set to underlying file.\n"); +out_free: + kmem_cache_free(ecryptfs_header_cache_0, page_virt); +out: + return rc; +} + +static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat, + char *virt, int *bytes_read) +{ + int rc = 0; + u32 header_extent_size; + u16 num_header_extents_at_front; + + memcpy(&header_extent_size, virt, 4); + header_extent_size = be32_to_cpu(header_extent_size); + virt += 4; + memcpy(&num_header_extents_at_front, virt, 2); + num_header_extents_at_front = be16_to_cpu(num_header_extents_at_front); + crypt_stat->header_extent_size = (int)header_extent_size; + crypt_stat->num_header_extents_at_front = + (int)num_header_extents_at_front; + (*bytes_read) = 6; + if ((crypt_stat->header_extent_size + * crypt_stat->num_header_extents_at_front) + < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { + rc = -EINVAL; + ecryptfs_printk(KERN_WARNING, "Invalid header extent size: " + "[%d]\n", crypt_stat->header_extent_size); + } + return rc; +} + +/** + * set_default_header_data + * + * For version 0 file format; this function is only for backwards + * compatibility for files created with the prior versions of + * eCryptfs. + */ +static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat) +{ + crypt_stat->header_extent_size = 4096; + crypt_stat->num_header_extents_at_front = 1; +} + +/** + * ecryptfs_read_headers_virt + * + * Read/parse the header data. The header format is detailed in the + * comment block for the ecryptfs_write_headers_virt() function. + * + * Returns zero on success + */ +static int ecryptfs_read_headers_virt(char *page_virt, + struct ecryptfs_crypt_stat *crypt_stat, + struct dentry *ecryptfs_dentry) +{ + int rc = 0; + int offset; + int bytes_read; + + ecryptfs_set_default_sizes(crypt_stat); + crypt_stat->mount_crypt_stat = &ecryptfs_superblock_to_private( + ecryptfs_dentry->d_sb)->mount_crypt_stat; + offset = ECRYPTFS_FILE_SIZE_BYTES; + rc = contains_ecryptfs_marker(page_virt + offset); + if (rc == 0) { + rc = -EINVAL; + goto out; + } + offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; + rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset), + &bytes_read); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error processing flags\n"); + goto out; + } + if (crypt_stat->file_version > ECRYPTFS_SUPPORTED_FILE_VERSION) { + ecryptfs_printk(KERN_WARNING, "File version is [%d]; only " + "file version [%d] is supported by this " + "version of eCryptfs\n", + crypt_stat->file_version, + ECRYPTFS_SUPPORTED_FILE_VERSION); + rc = -EINVAL; + goto out; + } + offset += bytes_read; + if (crypt_stat->file_version >= 1) { + rc = parse_header_metadata(crypt_stat, (page_virt + offset), + &bytes_read); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error reading header " + "metadata; rc = [%d]\n", rc); + } + offset += bytes_read; + } else + set_default_header_data(crypt_stat); + rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset), + ecryptfs_dentry); +out: + return rc; +} + +/** + * ecryptfs_read_headers + * + * Returns zero if valid headers found and parsed; non-zero otherwise + */ +int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file) +{ + int rc = 0; + char *page_virt = NULL; + mm_segment_t oldfs; + ssize_t bytes_read; + struct ecryptfs_crypt_stat *crypt_stat = + &ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat; + + /* Read the first page from the underlying file */ + page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER); + if (!page_virt) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Unable to allocate page_virt\n"); + goto out; + } + lower_file->f_pos = 0; + oldfs = get_fs(); + set_fs(get_ds()); + bytes_read = lower_file->f_op->read(lower_file, + (char __user *)page_virt, + ECRYPTFS_DEFAULT_EXTENT_SIZE, + &lower_file->f_pos); + set_fs(oldfs); + if (bytes_read != ECRYPTFS_DEFAULT_EXTENT_SIZE) { + rc = -EINVAL; + goto out; + } + rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, + ecryptfs_dentry); + if (rc) { + ecryptfs_printk(KERN_DEBUG, "Valid eCryptfs headers not " + "found\n"); + rc = -EINVAL; + } +out: + if (page_virt) { + memset(page_virt, 0, PAGE_CACHE_SIZE); + kmem_cache_free(ecryptfs_header_cache_1, page_virt); + } + return rc; +} + +/** + * ecryptfs_encode_filename - converts a plaintext file name to cipher text + * @crypt_stat: The crypt_stat struct associated with the file anem to encode + * @name: The plaintext name + * @length: The length of the plaintext + * @encoded_name: The encypted name + * + * Encrypts and encodes a filename into something that constitutes a + * valid filename for a filesystem, with printable characters. + * + * We assume that we have a properly initialized crypto context, + * pointed to by crypt_stat->tfm. + * + * TODO: Implement filename decoding and decryption here, in place of + * memcpy. We are keeping the framework around for now to (1) + * facilitate testing of the components needed to implement filename + * encryption and (2) to provide a code base from which other + * developers in the community can easily implement this feature. + * + * Returns the length of encoded filename; negative if error + */ +int +ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat, + const char *name, int length, char **encoded_name) +{ + int error = 0; + + (*encoded_name) = kmalloc(length + 2, GFP_KERNEL); + if (!(*encoded_name)) { + error = -ENOMEM; + goto out; + } + /* TODO: Filename encryption is a scheduled feature for a + * future version of eCryptfs. This function is here only for + * the purpose of providing a framework for other developers + * to easily implement filename encryption. Hint: Replace this + * memcpy() with a call to encrypt and encode the + * filename, the set the length accordingly. */ + memcpy((void *)(*encoded_name), (void *)name, length); + (*encoded_name)[length] = '\0'; + error = length + 1; +out: + return error; +} + +/** + * ecryptfs_decode_filename - converts the cipher text name to plaintext + * @crypt_stat: The crypt_stat struct associated with the file + * @name: The filename in cipher text + * @length: The length of the cipher text name + * @decrypted_name: The plaintext name + * + * Decodes and decrypts the filename. + * + * We assume that we have a properly initialized crypto context, + * pointed to by crypt_stat->tfm. + * + * TODO: Implement filename decoding and decryption here, in place of + * memcpy. We are keeping the framework around for now to (1) + * facilitate testing of the components needed to implement filename + * encryption and (2) to provide a code base from which other + * developers in the community can easily implement this feature. + * + * Returns the length of decoded filename; negative if error + */ +int +ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat, + const char *name, int length, char **decrypted_name) +{ + int error = 0; + + (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL); + if (!(*decrypted_name)) { + error = -ENOMEM; + goto out; + } + /* TODO: Filename encryption is a scheduled feature for a + * future version of eCryptfs. This function is here only for + * the purpose of providing a framework for other developers + * to easily implement filename encryption. Hint: Replace this + * memcpy() with a call to decode and decrypt the + * filename, the set the length accordingly. */ + memcpy((void *)(*decrypted_name), (void *)name, length); + (*decrypted_name)[length + 1] = '\0'; /* Only for convenience + * in printing out the + * string in debug + * messages */ + error = length; +out: + return error; +} + +/** + * ecryptfs_process_cipher - Perform cipher initialization. + * @tfm: Crypto context set by this function + * @key_tfm: Crypto context for key material, set by this function + * @cipher_name: Name of the cipher. + * @key_size: Size of the key in bytes. + * + * Returns zero on success. Any crypto_tfm structs allocated here + * should be released by other functions, such as on a superblock put + * event, regardless of whether this function succeeds for fails. + */ +int +ecryptfs_process_cipher(struct crypto_tfm **tfm, struct crypto_tfm **key_tfm, + char *cipher_name, size_t key_size) +{ + char dummy_key[ECRYPTFS_MAX_KEY_BYTES]; + int rc; + + *tfm = *key_tfm = NULL; + if (key_size > ECRYPTFS_MAX_KEY_BYTES) { + rc = -EINVAL; + printk(KERN_ERR "Requested key size is [%Zd] bytes; maximum " + "allowable is [%d]\n", key_size, ECRYPTFS_MAX_KEY_BYTES); + goto out; + } + *tfm = crypto_alloc_tfm(cipher_name, (ECRYPTFS_DEFAULT_CHAINING_MODE + | CRYPTO_TFM_REQ_WEAK_KEY)); + if (!(*tfm)) { + rc = -EINVAL; + printk(KERN_ERR "Unable to allocate crypto cipher with name " + "[%s]\n", cipher_name); + goto out; + } + *key_tfm = crypto_alloc_tfm(cipher_name, CRYPTO_TFM_REQ_WEAK_KEY); + if (!(*key_tfm)) { + rc = -EINVAL; + printk(KERN_ERR "Unable to allocate crypto cipher with name " + "[%s]\n", cipher_name); + goto out; + } + if (key_size < crypto_tfm_alg_min_keysize(*tfm)) { + rc = -EINVAL; + printk(KERN_ERR "Request key size is [%Zd]; minimum key size " + "supported by cipher [%s] is [%d]\n", key_size, + cipher_name, crypto_tfm_alg_min_keysize(*tfm)); + goto out; + } + if (key_size < crypto_tfm_alg_min_keysize(*key_tfm)) { + rc = -EINVAL; + printk(KERN_ERR "Request key size is [%Zd]; minimum key size " + "supported by cipher [%s] is [%d]\n", key_size, + cipher_name, crypto_tfm_alg_min_keysize(*key_tfm)); + goto out; + } + if (key_size > crypto_tfm_alg_max_keysize(*tfm)) { + rc = -EINVAL; + printk(KERN_ERR "Request key size is [%Zd]; maximum key size " + "supported by cipher [%s] is [%d]\n", key_size, + cipher_name, crypto_tfm_alg_min_keysize(*tfm)); + goto out; + } + if (key_size > crypto_tfm_alg_max_keysize(*key_tfm)) { + rc = -EINVAL; + printk(KERN_ERR "Request key size is [%Zd]; maximum key size " + "supported by cipher [%s] is [%d]\n", key_size, + cipher_name, crypto_tfm_alg_min_keysize(*key_tfm)); + goto out; + } + get_random_bytes(dummy_key, key_size); + rc = crypto_cipher_setkey(*tfm, dummy_key, key_size); + if (rc) { + printk(KERN_ERR "Error attempting to set key of size [%Zd] for " + "cipher [%s]; rc = [%d]\n", key_size, cipher_name, rc); + rc = -EINVAL; + goto out; + } + rc = crypto_cipher_setkey(*key_tfm, dummy_key, key_size); + if (rc) { + printk(KERN_ERR "Error attempting to set key of size [%Zd] for " + "cipher [%s]; rc = [%d]\n", key_size, cipher_name, rc); + rc = -EINVAL; + goto out; + } +out: + return rc; +} diff --git a/fs/ecryptfs/debug.c b/fs/ecryptfs/debug.c new file mode 100644 index 0000000000..61f8e89428 --- /dev/null +++ b/fs/ecryptfs/debug.c @@ -0,0 +1,123 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * Functions only useful for debugging. + * + * Copyright (C) 2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "ecryptfs_kernel.h" + +/** + * ecryptfs_dump_auth_tok - debug function to print auth toks + * + * This function will print the contents of an ecryptfs authentication + * token. + */ +void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok) +{ + char salt[ECRYPTFS_SALT_SIZE * 2 + 1]; + char sig[ECRYPTFS_SIG_SIZE_HEX + 1]; + + ecryptfs_printk(KERN_DEBUG, "Auth tok at mem loc [%p]:\n", + auth_tok); + if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PRIVATE_KEY)) { + ecryptfs_printk(KERN_DEBUG, " * private key type\n"); + ecryptfs_printk(KERN_DEBUG, " * (NO PRIVATE KEY SUPPORT " + "IN ECRYPTFS VERSION 0.1)\n"); + } else { + ecryptfs_printk(KERN_DEBUG, " * passphrase type\n"); + ecryptfs_to_hex(salt, auth_tok->token.password.salt, + ECRYPTFS_SALT_SIZE); + salt[ECRYPTFS_SALT_SIZE * 2] = '\0'; + ecryptfs_printk(KERN_DEBUG, " * salt = [%s]\n", salt); + if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags, + ECRYPTFS_PERSISTENT_PASSWORD)) { + ecryptfs_printk(KERN_DEBUG, " * persistent\n"); + } + memcpy(sig, auth_tok->token.password.signature, + ECRYPTFS_SIG_SIZE_HEX); + sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig); + } + ecryptfs_printk(KERN_DEBUG, " * session_key.flags = [0x%x]\n", + auth_tok->session_key.flags); + if (auth_tok->session_key.flags + & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT) + ecryptfs_printk(KERN_DEBUG, + " * Userspace decrypt request set\n"); + if (auth_tok->session_key.flags + & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT) + ecryptfs_printk(KERN_DEBUG, + " * Userspace encrypt request set\n"); + if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) { + ecryptfs_printk(KERN_DEBUG, " * Contains decrypted key\n"); + ecryptfs_printk(KERN_DEBUG, + " * session_key.decrypted_key_size = [0x%x]\n", + auth_tok->session_key.decrypted_key_size); + ecryptfs_printk(KERN_DEBUG, " * Decrypted session key " + "dump:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(auth_tok->session_key.decrypted_key, + ECRYPTFS_DEFAULT_KEY_BYTES); + } + if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_ENCRYPTED_KEY) { + ecryptfs_printk(KERN_DEBUG, " * Contains encrypted key\n"); + ecryptfs_printk(KERN_DEBUG, + " * session_key.encrypted_key_size = [0x%x]\n", + auth_tok->session_key.encrypted_key_size); + ecryptfs_printk(KERN_DEBUG, " * Encrypted session key " + "dump:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(auth_tok->session_key.encrypted_key, + auth_tok->session_key. + encrypted_key_size); + } +} + +/** + * ecryptfs_dump_hex - debug hex printer + * @data: string of bytes to be printed + * @bytes: number of bytes to print + * + * Dump hexadecimal representation of char array + */ +void ecryptfs_dump_hex(char *data, int bytes) +{ + int i = 0; + int add_newline = 1; + + if (ecryptfs_verbosity < 1) + return; + if (bytes != 0) { + printk(KERN_DEBUG "0x%.2x.", (unsigned char)data[i]); + i++; + } + while (i < bytes) { + printk("0x%.2x.", (unsigned char)data[i]); + i++; + if (i % 16 == 0) { + printk("\n"); + add_newline = 0; + } else + add_newline = 1; + } + if (add_newline) + printk("\n"); +} + diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c new file mode 100644 index 0000000000..f0d2a43324 --- /dev/null +++ b/fs/ecryptfs/dentry.c @@ -0,0 +1,87 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2003 Erez Zadok + * Copyright (C) 2001-2003 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include "ecryptfs_kernel.h" + +/** + * ecryptfs_d_revalidate - revalidate an ecryptfs dentry + * @dentry: The ecryptfs dentry + * @nd: The associated nameidata + * + * Called when the VFS needs to revalidate a dentry. This + * is called whenever a name lookup finds a dentry in the + * dcache. Most filesystems leave this as NULL, because all their + * dentries in the dcache are valid. + * + * Returns 1 if valid, 0 otherwise. + * + */ +static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + struct dentry *dentry_save; + struct vfsmount *vfsmount_save; + int rc = 1; + + if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) + goto out; + dentry_save = nd->dentry; + vfsmount_save = nd->mnt; + nd->dentry = lower_dentry; + nd->mnt = lower_mnt; + rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd); + nd->dentry = dentry_save; + nd->mnt = vfsmount_save; +out: + return rc; +} + +struct kmem_cache *ecryptfs_dentry_info_cache; + +/** + * ecryptfs_d_release + * @dentry: The ecryptfs dentry + * + * Called when a dentry is really deallocated. + */ +static void ecryptfs_d_release(struct dentry *dentry) +{ + struct dentry *lower_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (ecryptfs_dentry_to_private(dentry)) + kmem_cache_free(ecryptfs_dentry_info_cache, + ecryptfs_dentry_to_private(dentry)); + if (lower_dentry) + dput(lower_dentry); + return; +} + +struct dentry_operations ecryptfs_dops = { + .d_revalidate = ecryptfs_d_revalidate, + .d_release = ecryptfs_d_release, +}; diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h new file mode 100644 index 0000000000..872c995853 --- /dev/null +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -0,0 +1,482 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * Kernel declarations. + * + * Copyright (C) 1997-2003 Erez Zadok + * Copyright (C) 2001-2003 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef ECRYPTFS_KERNEL_H +#define ECRYPTFS_KERNEL_H + +#include +#include +#include + +/* Version verification for shared data structures w/ userspace */ +#define ECRYPTFS_VERSION_MAJOR 0x00 +#define ECRYPTFS_VERSION_MINOR 0x04 +#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x01 +/* These flags indicate which features are supported by the kernel + * module; userspace tools such as the mount helper read + * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine + * how to behave. */ +#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001 +#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002 +#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004 +#define ECRYPTFS_VERSIONING_POLICY 0x00000008 +#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \ + | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH) + +#define ECRYPTFS_MAX_PASSWORD_LENGTH 64 +#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH +#define ECRYPTFS_SALT_SIZE 8 +#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2) +/* The original signature size is only for what is stored on disk; all + * in-memory representations are expanded hex, so it better adapted to + * be passed around or referenced on the command line */ +#define ECRYPTFS_SIG_SIZE 8 +#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2) +#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX +#define ECRYPTFS_MAX_KEY_BYTES 64 +#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512 +#define ECRYPTFS_DEFAULT_IV_BYTES 16 +#define ECRYPTFS_FILE_VERSION 0x01 +#define ECRYPTFS_DEFAULT_HEADER_EXTENT_SIZE 8192 +#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096 +#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192 + +#define RFC2440_CIPHER_DES3_EDE 0x02 +#define RFC2440_CIPHER_CAST_5 0x03 +#define RFC2440_CIPHER_BLOWFISH 0x04 +#define RFC2440_CIPHER_AES_128 0x07 +#define RFC2440_CIPHER_AES_192 0x08 +#define RFC2440_CIPHER_AES_256 0x09 +#define RFC2440_CIPHER_TWOFISH 0x0a +#define RFC2440_CIPHER_CAST_6 0x0b + +#define ECRYPTFS_SET_FLAG(flag_bit_vector, flag) (flag_bit_vector |= (flag)) +#define ECRYPTFS_CLEAR_FLAG(flag_bit_vector, flag) (flag_bit_vector &= ~(flag)) +#define ECRYPTFS_CHECK_FLAG(flag_bit_vector, flag) (flag_bit_vector & (flag)) + +/** + * For convenience, we may need to pass around the encrypted session + * key between kernel and userspace because the authentication token + * may not be extractable. For example, the TPM may not release the + * private key, instead requiring the encrypted data and returning the + * decrypted data. + */ +struct ecryptfs_session_key { +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001 +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002 +#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004 +#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008 + u32 flags; + u32 encrypted_key_size; + u32 decrypted_key_size; + u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; + u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES]; +}; + +struct ecryptfs_password { + u32 password_bytes; + s32 hash_algo; + u32 hash_iterations; + u32 session_key_encryption_key_bytes; +#define ECRYPTFS_PERSISTENT_PASSWORD 0x01 +#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02 + u32 flags; + /* Iterated-hash concatenation of salt and passphrase */ + u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; + u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; + /* Always in expanded hex */ + u8 salt[ECRYPTFS_SALT_SIZE]; +}; + +enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY}; + +/* May be a password or a private key */ +struct ecryptfs_auth_tok { + u16 version; /* 8-bit major and 8-bit minor */ + u16 token_type; + u32 flags; + struct ecryptfs_session_key session_key; + u8 reserved[32]; + union { + struct ecryptfs_password password; + /* Private key is in future eCryptfs releases */ + } token; +} __attribute__ ((packed)); + +void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok); +extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size); +extern void ecryptfs_from_hex(char *dst, char *src, int dst_size); + +struct ecryptfs_key_record { + unsigned char type; + size_t enc_key_size; + unsigned char sig[ECRYPTFS_SIG_SIZE]; + unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; +}; + +struct ecryptfs_auth_tok_list { + struct ecryptfs_auth_tok *auth_tok; + struct list_head list; +}; + +struct ecryptfs_crypt_stat; +struct ecryptfs_mount_crypt_stat; + +struct ecryptfs_page_crypt_context { + struct page *page; +#define ECRYPTFS_PREPARE_COMMIT_MODE 0 +#define ECRYPTFS_WRITEPAGE_MODE 1 + unsigned int mode; + union { + struct file *lower_file; + struct writeback_control *wbc; + } param; +}; + +static inline struct ecryptfs_auth_tok * +ecryptfs_get_key_payload_data(struct key *key) +{ + return (struct ecryptfs_auth_tok *) + (((struct user_key_payload*)key->payload.data)->data); +} + +#define ECRYPTFS_SUPER_MAGIC 0xf15f +#define ECRYPTFS_MAX_KEYSET_SIZE 1024 +#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32 +#define ECRYPTFS_MAX_NUM_ENC_KEYS 64 +#define ECRYPTFS_MAX_NUM_KEYSIGS 2 /* TODO: Make this a linked list */ +#define ECRYPTFS_MAX_IV_BYTES 16 /* 128 bits */ +#define ECRYPTFS_SALT_BYTES 2 +#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5 +#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8 /* 4*2 */ +#define ECRYPTFS_FILE_SIZE_BYTES 8 +#define ECRYPTFS_DEFAULT_CIPHER "aes" +#define ECRYPTFS_DEFAULT_KEY_BYTES 16 +#define ECRYPTFS_DEFAULT_CHAINING_MODE CRYPTO_TFM_MODE_CBC +#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C +#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED +#define MD5_DIGEST_SIZE 16 + +/** + * This is the primary struct associated with each encrypted file. + * + * TODO: cache align/pack? + */ +struct ecryptfs_crypt_stat { +#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001 +#define ECRYPTFS_POLICY_APPLIED 0x00000002 +#define ECRYPTFS_NEW_FILE 0x00000004 +#define ECRYPTFS_ENCRYPTED 0x00000008 +#define ECRYPTFS_SECURITY_WARNING 0x00000010 +#define ECRYPTFS_ENABLE_HMAC 0x00000020 +#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040 +#define ECRYPTFS_KEY_VALID 0x00000080 + u32 flags; + unsigned int file_version; + size_t iv_bytes; + size_t num_keysigs; + size_t header_extent_size; + size_t num_header_extents_at_front; + size_t extent_size; /* Data extent size; default is 4096 */ + size_t key_size; + size_t extent_shift; + unsigned int extent_mask; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat; + struct crypto_tfm *tfm; + struct crypto_tfm *md5_tfm; /* Crypto context for generating + * the initialization vectors */ + unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE]; + unsigned char key[ECRYPTFS_MAX_KEY_BYTES]; + unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES]; + unsigned char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX]; + struct mutex cs_tfm_mutex; + struct mutex cs_md5_tfm_mutex; + struct mutex cs_mutex; +}; + +/* inode private data. */ +struct ecryptfs_inode_info { + struct inode vfs_inode; + struct inode *wii_inode; + struct ecryptfs_crypt_stat crypt_stat; +}; + +/* dentry private data. Each dentry must keep track of a lower + * vfsmount too. */ +struct ecryptfs_dentry_info { + struct dentry *wdi_dentry; + struct vfsmount *lower_mnt; + struct ecryptfs_crypt_stat *crypt_stat; +}; + +/** + * This struct is to enable a mount-wide passphrase/salt combo. This + * is more or less a stopgap to provide similar functionality to other + * crypto filesystems like EncFS or CFS until full policy support is + * implemented in eCryptfs. + */ +struct ecryptfs_mount_crypt_stat { + /* Pointers to memory we do not own, do not free these */ +#define ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED 0x00000001 + u32 flags; + struct ecryptfs_auth_tok *global_auth_tok; + struct key *global_auth_tok_key; + size_t global_default_cipher_key_size; + struct crypto_tfm *global_key_tfm; + struct mutex global_key_tfm_mutex; + unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + + 1]; + unsigned char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1]; +}; + +/* superblock private data. */ +struct ecryptfs_sb_info { + struct super_block *wsi_sb; + struct ecryptfs_mount_crypt_stat mount_crypt_stat; +}; + +/* file private data. */ +struct ecryptfs_file_info { + struct file *wfi_file; + struct ecryptfs_crypt_stat *crypt_stat; +}; + +/* auth_tok <=> encrypted_session_key mappings */ +struct ecryptfs_auth_tok_list_item { + unsigned char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES]; + struct list_head list; + struct ecryptfs_auth_tok auth_tok; +}; + +static inline struct ecryptfs_file_info * +ecryptfs_file_to_private(struct file *file) +{ + return (struct ecryptfs_file_info *)file->private_data; +} + +static inline void +ecryptfs_set_file_private(struct file *file, + struct ecryptfs_file_info *file_info) +{ + file->private_data = file_info; +} + +static inline struct file *ecryptfs_file_to_lower(struct file *file) +{ + return ((struct ecryptfs_file_info *)file->private_data)->wfi_file; +} + +static inline void +ecryptfs_set_file_lower(struct file *file, struct file *lower_file) +{ + ((struct ecryptfs_file_info *)file->private_data)->wfi_file = + lower_file; +} + +static inline struct ecryptfs_inode_info * +ecryptfs_inode_to_private(struct inode *inode) +{ + return container_of(inode, struct ecryptfs_inode_info, vfs_inode); +} + +static inline struct inode *ecryptfs_inode_to_lower(struct inode *inode) +{ + return ecryptfs_inode_to_private(inode)->wii_inode; +} + +static inline void +ecryptfs_set_inode_lower(struct inode *inode, struct inode *lower_inode) +{ + ecryptfs_inode_to_private(inode)->wii_inode = lower_inode; +} + +static inline struct ecryptfs_sb_info * +ecryptfs_superblock_to_private(struct super_block *sb) +{ + return (struct ecryptfs_sb_info *)sb->s_fs_info; +} + +static inline void +ecryptfs_set_superblock_private(struct super_block *sb, + struct ecryptfs_sb_info *sb_info) +{ + sb->s_fs_info = sb_info; +} + +static inline struct super_block * +ecryptfs_superblock_to_lower(struct super_block *sb) +{ + return ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb; +} + +static inline void +ecryptfs_set_superblock_lower(struct super_block *sb, + struct super_block *lower_sb) +{ + ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb = lower_sb; +} + +static inline struct ecryptfs_dentry_info * +ecryptfs_dentry_to_private(struct dentry *dentry) +{ + return (struct ecryptfs_dentry_info *)dentry->d_fsdata; +} + +static inline void +ecryptfs_set_dentry_private(struct dentry *dentry, + struct ecryptfs_dentry_info *dentry_info) +{ + dentry->d_fsdata = dentry_info; +} + +static inline struct dentry * +ecryptfs_dentry_to_lower(struct dentry *dentry) +{ + return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry; +} + +static inline void +ecryptfs_set_dentry_lower(struct dentry *dentry, struct dentry *lower_dentry) +{ + ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry = + lower_dentry; +} + +static inline struct vfsmount * +ecryptfs_dentry_to_lower_mnt(struct dentry *dentry) +{ + return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt; +} + +static inline void +ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt) +{ + ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt = + lower_mnt; +} + +#define ecryptfs_printk(type, fmt, arg...) \ + __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg); +void __ecryptfs_printk(const char *fmt, ...); + +extern const struct file_operations ecryptfs_main_fops; +extern const struct file_operations ecryptfs_dir_fops; +extern struct inode_operations ecryptfs_main_iops; +extern struct inode_operations ecryptfs_dir_iops; +extern struct inode_operations ecryptfs_symlink_iops; +extern struct super_operations ecryptfs_sops; +extern struct dentry_operations ecryptfs_dops; +extern struct address_space_operations ecryptfs_aops; +extern int ecryptfs_verbosity; + +extern struct kmem_cache *ecryptfs_auth_tok_list_item_cache; +extern struct kmem_cache *ecryptfs_file_info_cache; +extern struct kmem_cache *ecryptfs_dentry_info_cache; +extern struct kmem_cache *ecryptfs_inode_info_cache; +extern struct kmem_cache *ecryptfs_sb_info_cache; +extern struct kmem_cache *ecryptfs_header_cache_0; +extern struct kmem_cache *ecryptfs_header_cache_1; +extern struct kmem_cache *ecryptfs_header_cache_2; +extern struct kmem_cache *ecryptfs_lower_page_cache; + +int ecryptfs_interpose(struct dentry *hidden_dentry, + struct dentry *this_dentry, struct super_block *sb, + int flag); +int ecryptfs_fill_zeros(struct file *file, loff_t new_length); +int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat, + const char *name, int length, + char **decrypted_name); +int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat, + const char *name, int length, + char **encoded_name); +struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry); +void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src); +void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src); +void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src); +void ecryptfs_dump_hex(char *data, int bytes); +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, + int sg_size); +int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat); +void ecryptfs_rotate_iv(unsigned char *iv); +void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat); +void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat); +void ecryptfs_destruct_mount_crypt_stat( + struct ecryptfs_mount_crypt_stat *mount_crypt_stat); +int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat); +int ecryptfs_write_inode_size_to_header(struct file *lower_file, + struct inode *lower_inode, + struct inode *inode); +int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode, + struct file *lower_file, + unsigned long lower_page_index, int byte_offset, + int region_bytes); +int +ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode, + struct file *lower_file, int byte_offset, + int region_size); +int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode, + struct file *lower_file); +int ecryptfs_do_readpage(struct file *file, struct page *page, + pgoff_t lower_page_index); +int ecryptfs_grab_and_map_lower_page(struct page **lower_page, + char **lower_virt, + struct inode *lower_inode, + unsigned long lower_page_index); +int ecryptfs_writepage_and_release_lower_page(struct page *lower_page, + struct inode *lower_inode, + struct writeback_control *wbc); +int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx); +int ecryptfs_decrypt_page(struct file *file, struct page *page); +int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file); +int ecryptfs_write_headers_virt(char *page_virt, + struct ecryptfs_crypt_stat *crypt_stat, + struct dentry *ecryptfs_dentry); +int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file); +int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry); +int contains_ecryptfs_marker(char *data); +int ecryptfs_read_header_region(char *data, struct dentry *dentry, + struct vfsmount *mnt); +u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat); +int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code); +void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat); +int ecryptfs_generate_key_packet_set(char *dest_base, + struct ecryptfs_crypt_stat *crypt_stat, + struct dentry *ecryptfs_dentry, + size_t *len, size_t max); +int process_request_key_err(long err_code); +int +ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, + unsigned char *src, struct dentry *ecryptfs_dentry); +int ecryptfs_truncate(struct dentry *dentry, loff_t new_length); +int +ecryptfs_process_cipher(struct crypto_tfm **tfm, struct crypto_tfm **key_tfm, + char *cipher_name, size_t key_size); +int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode); +int ecryptfs_inode_set(struct inode *inode, void *lower_inode); +void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode); + +#endif /* #ifndef ECRYPTFS_KERNEL_H */ diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c new file mode 100644 index 0000000000..c8550c9f9c --- /dev/null +++ b/fs/ecryptfs/file.c @@ -0,0 +1,440 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2004 Erez Zadok + * Copyright (C) 2001-2004 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * ecryptfs_llseek + * @file: File we are seeking in + * @offset: The offset to seek to + * @origin: 2 - offset from i_size; 1 - offset from f_pos + * + * Returns the position we have seeked to, or negative on error + */ +static loff_t ecryptfs_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t rv; + loff_t new_end_pos; + int rc; + int expanding_file = 0; + struct inode *inode = file->f_mapping->host; + + /* If our offset is past the end of our file, we're going to + * need to grow it so we have a valid length of 0's */ + new_end_pos = offset; + switch (origin) { + case 2: + new_end_pos += i_size_read(inode); + expanding_file = 1; + break; + case 1: + new_end_pos += file->f_pos; + if (new_end_pos > i_size_read(inode)) { + ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) " + "> i_size_read(inode)(=[0x%.16x])\n", + new_end_pos, i_size_read(inode)); + expanding_file = 1; + } + break; + default: + if (new_end_pos > i_size_read(inode)) { + ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) " + "> i_size_read(inode)(=[0x%.16x])\n", + new_end_pos, i_size_read(inode)); + expanding_file = 1; + } + } + ecryptfs_printk(KERN_DEBUG, "new_end_pos = [0x%.16x]\n", new_end_pos); + if (expanding_file) { + rc = ecryptfs_truncate(file->f_dentry, new_end_pos); + if (rc) { + rv = rc; + ecryptfs_printk(KERN_ERR, "Error on attempt to " + "truncate to (higher) offset [0x%.16x];" + " rc = [%d]\n", new_end_pos, rc); + goto out; + } + } + rv = generic_file_llseek(file, offset, origin); +out: + return rv; +} + +/** + * ecryptfs_read_update_atime + * + * generic_file_read updates the atime of upper layer inode. But, it + * doesn't give us a chance to update the atime of the lower layer + * inode. This function is a wrapper to generic_file_read. It + * updates the atime of the lower level inode if generic_file_read + * returns without any errors. This is to be used only for file reads. + * The function to be used for directory reads is ecryptfs_read. + */ +static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, + const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + int rc; + struct dentry *lower_dentry; + struct vfsmount *lower_vfsmount; + struct file *file = iocb->ki_filp; + + rc = generic_file_aio_read(iocb, iov, nr_segs, pos); + /* + * Even though this is a async interface, we need to wait + * for IO to finish to update atime + */ + if (-EIOCBQUEUED == rc) + rc = wait_on_sync_kiocb(iocb); + if (rc >= 0) { + lower_dentry = ecryptfs_dentry_to_lower(file->f_dentry); + lower_vfsmount = ecryptfs_dentry_to_lower_mnt(file->f_dentry); + touch_atime(lower_vfsmount, lower_dentry); + } + return rc; +} + +struct ecryptfs_getdents_callback { + void *dirent; + struct dentry *dentry; + filldir_t filldir; + int err; + int filldir_called; + int entries_written; +}; + +/* Inspired by generic filldir in fs/readir.c */ +static int +ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset, + u64 ino, unsigned int d_type) +{ + struct ecryptfs_crypt_stat *crypt_stat; + struct ecryptfs_getdents_callback *buf = + (struct ecryptfs_getdents_callback *)dirent; + int rc; + int decoded_length; + char *decoded_name; + + crypt_stat = ecryptfs_dentry_to_private(buf->dentry)->crypt_stat; + buf->filldir_called++; + decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen, + &decoded_name); + if (decoded_length < 0) { + rc = decoded_length; + goto out; + } + rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset, + ino, d_type); + kfree(decoded_name); + if (rc >= 0) + buf->entries_written++; +out: + return rc; +} + +/** + * ecryptfs_readdir + * @file: The ecryptfs file struct + * @dirent: Directory entry + * @filldir: The filldir callback function + */ +static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int rc; + struct file *lower_file; + struct inode *inode; + struct ecryptfs_getdents_callback buf; + + lower_file = ecryptfs_file_to_lower(file); + lower_file->f_pos = file->f_pos; + inode = file->f_dentry->d_inode; + memset(&buf, 0, sizeof(buf)); + buf.dirent = dirent; + buf.dentry = file->f_dentry; + buf.filldir = filldir; +retry: + buf.filldir_called = 0; + buf.entries_written = 0; + buf.err = 0; + rc = vfs_readdir(lower_file, ecryptfs_filldir, (void *)&buf); + if (buf.err) + rc = buf.err; + if (buf.filldir_called && !buf.entries_written) + goto retry; + file->f_pos = lower_file->f_pos; + if (rc >= 0) + ecryptfs_copy_attr_atime(inode, lower_file->f_dentry->d_inode); + return rc; +} + +struct kmem_cache *ecryptfs_file_info_cache; + +/** + * ecryptfs_open + * @inode: inode speciying file to open + * @file: Structure to return filled in + * + * Opens the file specified by inode. + * + * Returns zero on success; non-zero otherwise + */ +static int ecryptfs_open(struct inode *inode, struct file *file) +{ + int rc = 0; + struct ecryptfs_crypt_stat *crypt_stat = NULL; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat; + struct dentry *ecryptfs_dentry = file->f_dentry; + /* Private value of ecryptfs_dentry allocated in + * ecryptfs_lookup() */ + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); + struct inode *lower_inode = NULL; + struct file *lower_file = NULL; + struct vfsmount *lower_mnt; + struct ecryptfs_file_info *file_info; + int lower_flags; + + /* Released in ecryptfs_release or end of function if failure */ + file_info = kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL); + ecryptfs_set_file_private(file, file_info); + if (!file_info) { + ecryptfs_printk(KERN_ERR, + "Error attempting to allocate memory\n"); + rc = -ENOMEM; + goto out; + } + memset(file_info, 0, sizeof(*file_info)); + lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + mount_crypt_stat = &ecryptfs_superblock_to_private( + ecryptfs_dentry->d_sb)->mount_crypt_stat; + mutex_lock(&crypt_stat->cs_mutex); + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) { + ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n"); + /* Policy code enabled in future release */ + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); + } + mutex_unlock(&crypt_stat->cs_mutex); + /* This mntget & dget is undone via fput when the file is released */ + dget(lower_dentry); + lower_flags = file->f_flags; + if ((lower_flags & O_ACCMODE) == O_WRONLY) + lower_flags = (lower_flags & O_ACCMODE) | O_RDWR; + if (file->f_flags & O_APPEND) + lower_flags &= ~O_APPEND; + lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); + mntget(lower_mnt); + /* Corresponding fput() in ecryptfs_release() */ + lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags); + if (IS_ERR(lower_file)) { + rc = PTR_ERR(lower_file); + ecryptfs_printk(KERN_ERR, "Error opening lower file\n"); + goto out_puts; + } + ecryptfs_set_file_lower(file, lower_file); + /* Isn't this check the same as the one in lookup? */ + lower_inode = lower_dentry->d_inode; + if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { + ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); + rc = 0; + goto out; + } + mutex_lock(&crypt_stat->cs_mutex); + if (i_size_read(lower_inode) < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { + if (!(mount_crypt_stat->flags + & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { + rc = -EIO; + printk(KERN_WARNING "Attempt to read file that is " + "not in a valid eCryptfs format, and plaintext " + "passthrough mode is not enabled; returning " + "-EIO\n"); + mutex_unlock(&crypt_stat->cs_mutex); + goto out_puts; + } + crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); + rc = 0; + mutex_unlock(&crypt_stat->cs_mutex); + goto out; + } else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ECRYPTFS_POLICY_APPLIED) + || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, + ECRYPTFS_KEY_VALID)) { + rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file); + if (rc) { + ecryptfs_printk(KERN_DEBUG, + "Valid headers not found\n"); + if (!(mount_crypt_stat->flags + & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { + rc = -EIO; + printk(KERN_WARNING "Attempt to read file that " + "is not in a valid eCryptfs format, " + "and plaintext passthrough mode is not " + "enabled; returning -EIO\n"); + mutex_unlock(&crypt_stat->cs_mutex); + goto out_puts; + } + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, + ECRYPTFS_ENCRYPTED); + rc = 0; + mutex_unlock(&crypt_stat->cs_mutex); + goto out; + } + } + mutex_unlock(&crypt_stat->cs_mutex); + ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] " + "size: [0x%.16x]\n", inode, inode->i_ino, + i_size_read(inode)); + ecryptfs_set_file_lower(file, lower_file); + goto out; +out_puts: + mntput(lower_mnt); + dput(lower_dentry); + kmem_cache_free(ecryptfs_file_info_cache, + ecryptfs_file_to_private(file)); +out: + return rc; +} + +static int ecryptfs_flush(struct file *file, fl_owner_t td) +{ + int rc = 0; + struct file *lower_file = NULL; + + lower_file = ecryptfs_file_to_lower(file); + if (lower_file->f_op && lower_file->f_op->flush) + rc = lower_file->f_op->flush(lower_file, td); + return rc; +} + +static int ecryptfs_release(struct inode *inode, struct file *file) +{ + struct file *lower_file = ecryptfs_file_to_lower(file); + struct ecryptfs_file_info *file_info = ecryptfs_file_to_private(file); + struct inode *lower_inode = ecryptfs_inode_to_lower(inode); + + fput(lower_file); + inode->i_blocks = lower_inode->i_blocks; + kmem_cache_free(ecryptfs_file_info_cache, file_info); + return 0; +} + +static int +ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct file *lower_file = ecryptfs_file_to_lower(file); + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + struct inode *lower_inode = lower_dentry->d_inode; + int rc = -EINVAL; + + if (lower_inode->i_fop->fsync) { + mutex_lock(&lower_inode->i_mutex); + rc = lower_inode->i_fop->fsync(lower_file, lower_dentry, + datasync); + mutex_unlock(&lower_inode->i_mutex); + } + return rc; +} + +static int ecryptfs_fasync(int fd, struct file *file, int flag) +{ + int rc = 0; + struct file *lower_file = NULL; + + lower_file = ecryptfs_file_to_lower(file); + if (lower_file->f_op && lower_file->f_op->fasync) + rc = lower_file->f_op->fasync(fd, lower_file, flag); + return rc; +} + +static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos, + size_t count, read_actor_t actor, void *target) +{ + struct file *lower_file = NULL; + int rc = -EINVAL; + + lower_file = ecryptfs_file_to_lower(file); + if (lower_file->f_op && lower_file->f_op->sendfile) + rc = lower_file->f_op->sendfile(lower_file, ppos, count, + actor, target); + + return rc; +} + +static int ecryptfs_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +const struct file_operations ecryptfs_dir_fops = { + .readdir = ecryptfs_readdir, + .ioctl = ecryptfs_ioctl, + .mmap = generic_file_mmap, + .open = ecryptfs_open, + .flush = ecryptfs_flush, + .release = ecryptfs_release, + .fsync = ecryptfs_fsync, + .fasync = ecryptfs_fasync, + .sendfile = ecryptfs_sendfile, +}; + +const struct file_operations ecryptfs_main_fops = { + .llseek = ecryptfs_llseek, + .read = do_sync_read, + .aio_read = ecryptfs_read_update_atime, + .write = do_sync_write, + .aio_write = generic_file_aio_write, + .readdir = ecryptfs_readdir, + .ioctl = ecryptfs_ioctl, + .mmap = generic_file_mmap, + .open = ecryptfs_open, + .flush = ecryptfs_flush, + .release = ecryptfs_release, + .fsync = ecryptfs_fsync, + .fasync = ecryptfs_fasync, + .sendfile = ecryptfs_sendfile, +}; + +static int +ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + struct file *lower_file = NULL; + + if (ecryptfs_file_to_private(file)) + lower_file = ecryptfs_file_to_lower(file); + if (lower_file && lower_file->f_op && lower_file->f_op->ioctl) + rc = lower_file->f_op->ioctl(ecryptfs_inode_to_lower(inode), + lower_file, cmd, arg); + else + rc = -ENOTTY; + return rc; +} diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c new file mode 100644 index 0000000000..efdd2b7b62 --- /dev/null +++ b/fs/ecryptfs/inode.c @@ -0,0 +1,1079 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2004 Erez Zadok + * Copyright (C) 2001-2004 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompsion + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +static struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir; + + dir = dget(dentry->d_parent); + mutex_lock(&(dir->d_inode->i_mutex)); + return dir; +} + +static void unlock_parent(struct dentry *dentry) +{ + mutex_unlock(&(dentry->d_parent->d_inode->i_mutex)); + dput(dentry->d_parent); +} + +static void unlock_dir(struct dentry *dir) +{ + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); +} + +void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src) +{ + i_size_write(dst, i_size_read((struct inode *)src)); + dst->i_blocks = src->i_blocks; +} + +void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src) +{ + dest->i_atime = src->i_atime; +} + +static void ecryptfs_copy_attr_times(struct inode *dest, + const struct inode *src) +{ + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; +} + +static void ecryptfs_copy_attr_timesizes(struct inode *dest, + const struct inode *src) +{ + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + ecryptfs_copy_inode_size(dest, src); +} + +void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src) +{ + dest->i_mode = src->i_mode; + dest->i_nlink = src->i_nlink; + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; +} + +/** + * ecryptfs_create_underlying_file + * @lower_dir_inode: inode of the parent in the lower fs of the new file + * @lower_dentry: New file's dentry in the lower fs + * @ecryptfs_dentry: New file's dentry in ecryptfs + * @mode: The mode of the new file + * @nd: nameidata of ecryptfs' parent's dentry & vfsmount + * + * Creates the file in the lower file system. + * + * Returns zero on success; non-zero on error condition + */ +static int +ecryptfs_create_underlying_file(struct inode *lower_dir_inode, + struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + struct dentry *dentry_save; + struct vfsmount *vfsmount_save; + int rc; + + dentry_save = nd->dentry; + vfsmount_save = nd->mnt; + nd->dentry = lower_dentry; + nd->mnt = lower_mnt; + rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); + nd->dentry = dentry_save; + nd->mnt = vfsmount_save; + return rc; +} + +/** + * ecryptfs_do_create + * @directory_inode: inode of the new file's dentry's parent in ecryptfs + * @ecryptfs_dentry: New file's dentry in ecryptfs + * @mode: The mode of the new file + * @nd: nameidata of ecryptfs' parent's dentry & vfsmount + * + * Creates the underlying file and the eCryptfs inode which will link to + * it. It will also update the eCryptfs directory inode to mimic the + * stat of the lower directory inode. + * + * Returns zero on success; non-zero on error condition + */ +static int +ecryptfs_do_create(struct inode *directory_inode, + struct dentry *ecryptfs_dentry, int mode, + struct nameidata *nd) +{ + int rc; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); + lower_dir_dentry = lock_parent(lower_dentry); + if (unlikely(IS_ERR(lower_dir_dentry))) { + ecryptfs_printk(KERN_ERR, "Error locking directory of " + "dentry\n"); + rc = PTR_ERR(lower_dir_dentry); + goto out; + } + rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode, + ecryptfs_dentry, mode, nd); + if (unlikely(rc)) { + ecryptfs_printk(KERN_ERR, + "Failure to create underlying file\n"); + goto out_lock; + } + rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, + directory_inode->i_sb, 0); + if (rc) { + ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n"); + goto out_lock; + } + ecryptfs_copy_attr_timesizes(directory_inode, + lower_dir_dentry->d_inode); +out_lock: + unlock_dir(lower_dir_dentry); +out: + return rc; +} + +/** + * grow_file + * @ecryptfs_dentry: the ecryptfs dentry + * @lower_file: The lower file + * @inode: The ecryptfs inode + * @lower_inode: The lower inode + * + * This is the code which will grow the file to its correct size. + */ +static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file, + struct inode *inode, struct inode *lower_inode) +{ + int rc = 0; + struct file fake_file; + struct ecryptfs_file_info tmp_file_info; + + memset(&fake_file, 0, sizeof(fake_file)); + fake_file.f_dentry = ecryptfs_dentry; + memset(&tmp_file_info, 0, sizeof(tmp_file_info)); + ecryptfs_set_file_private(&fake_file, &tmp_file_info); + ecryptfs_set_file_lower(&fake_file, lower_file); + rc = ecryptfs_fill_zeros(&fake_file, 1); + if (rc) { + ECRYPTFS_SET_FLAG( + ecryptfs_inode_to_private(inode)->crypt_stat.flags, + ECRYPTFS_SECURITY_WARNING); + ecryptfs_printk(KERN_WARNING, "Error attempting to fill zeros " + "in file; rc = [%d]\n", rc); + goto out; + } + i_size_write(inode, 0); + ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode); + ECRYPTFS_SET_FLAG(ecryptfs_inode_to_private(inode)->crypt_stat.flags, + ECRYPTFS_NEW_FILE); +out: + return rc; +} + +/** + * ecryptfs_initialize_file + * + * Cause the file to be changed from a basic empty file to an ecryptfs + * file with a header and first data page. + * + * Returns zero on success + */ +static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry) +{ + int rc = 0; + int lower_flags; + struct ecryptfs_crypt_stat *crypt_stat; + struct dentry *lower_dentry; + struct dentry *tlower_dentry = NULL; + struct file *lower_file; + struct inode *inode, *lower_inode; + struct vfsmount *lower_mnt; + + lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); + ecryptfs_printk(KERN_DEBUG, "lower_dentry->d_name.name = [%s]\n", + lower_dentry->d_name.name); + inode = ecryptfs_dentry->d_inode; + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + tlower_dentry = dget(lower_dentry); + if (!tlower_dentry) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry\n"); + goto out; + } + lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR; +#if BITS_PER_LONG != 32 + lower_flags |= O_LARGEFILE; +#endif + lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); + mntget(lower_mnt); + /* Corresponding fput() at end of this function */ + lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags); + if (IS_ERR(lower_file)) { + rc = PTR_ERR(lower_file); + ecryptfs_printk(KERN_ERR, + "Error opening dentry; rc = [%i]\n", rc); + goto out; + } + /* fput(lower_file) should handle the puts if we do this */ + lower_file->f_dentry = tlower_dentry; + lower_file->f_vfsmnt = lower_mnt; + lower_inode = tlower_dentry->d_inode; + if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { + ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); + goto out_fput; + } + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE); + ecryptfs_printk(KERN_DEBUG, "Initializing crypto context\n"); + rc = ecryptfs_new_file_context(ecryptfs_dentry); + if (rc) { + ecryptfs_printk(KERN_DEBUG, "Error creating new file " + "context\n"); + goto out_fput; + } + rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file); + if (rc) { + ecryptfs_printk(KERN_DEBUG, "Error writing headers\n"); + goto out_fput; + } + rc = grow_file(ecryptfs_dentry, lower_file, inode, lower_inode); +out_fput: + fput(lower_file); +out: + return rc; +} + +/** + * ecryptfs_create + * @dir: The inode of the directory in which to create the file. + * @dentry: The eCryptfs dentry + * @mode: The mode of the new file. + * @nd: nameidata + * + * Creates a new file. + * + * Returns zero on success; non-zero on error condition + */ +static int +ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, + int mode, struct nameidata *nd) +{ + int rc; + + rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd); + if (unlikely(rc)) { + ecryptfs_printk(KERN_WARNING, "Failed to create file in" + "lower filesystem\n"); + goto out; + } + /* At this point, a file exists on "disk"; we need to make sure + * that this on disk file is prepared to be an ecryptfs file */ + rc = ecryptfs_initialize_file(ecryptfs_dentry); +out: + return rc; +} + +/** + * ecryptfs_lookup + * @dir: inode + * @dentry: The dentry + * @nd: nameidata, may be NULL + * + * Find a file on disk. If the file does not exist, then we'll add it to the + * dentry cache and continue on to read it from the disk. + */ +static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + int rc = 0; + struct dentry *lower_dir_dentry; + struct dentry *lower_dentry; + struct vfsmount *lower_mnt; + struct dentry *tlower_dentry = NULL; + char *encoded_name; + unsigned int encoded_namelen; + struct ecryptfs_crypt_stat *crypt_stat = NULL; + char *page_virt = NULL; + struct inode *lower_inode; + u64 file_size; + + lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent); + dentry->d_op = &ecryptfs_dops; + if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, ".")) + || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, ".."))) + goto out_drop; + encoded_namelen = ecryptfs_encode_filename(crypt_stat, + dentry->d_name.name, + dentry->d_name.len, + &encoded_name); + if (encoded_namelen < 0) { + rc = encoded_namelen; + goto out_drop; + } + ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen " + "= [%d]\n", encoded_name, encoded_namelen); + lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, + encoded_namelen - 1); + kfree(encoded_name); + lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); + if (IS_ERR(lower_dentry)) { + ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n"); + rc = PTR_ERR(lower_dentry); + goto out_drop; + } + ecryptfs_printk(KERN_DEBUG, "lower_dentry = [%p]; lower_dentry->" + "d_name.name = [%s]\n", lower_dentry, + lower_dentry->d_name.name); + lower_inode = lower_dentry->d_inode; + ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode); + BUG_ON(!atomic_read(&lower_dentry->d_count)); + ecryptfs_set_dentry_private(dentry, + kmem_cache_alloc(ecryptfs_dentry_info_cache, + SLAB_KERNEL)); + if (!ecryptfs_dentry_to_private(dentry)) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting " + "to allocate ecryptfs_dentry_info struct\n"); + goto out_dput; + } + ecryptfs_set_dentry_lower(dentry, lower_dentry); + ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt); + if (!lower_dentry->d_inode) { + /* We want to add because we couldn't find in lower */ + d_add(dentry, NULL); + goto out; + } + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error interposing\n"); + goto out_dput; + } + if (S_ISDIR(lower_inode->i_mode)) { + ecryptfs_printk(KERN_DEBUG, "Is a directory; returning\n"); + goto out; + } + if (S_ISLNK(lower_inode->i_mode)) { + ecryptfs_printk(KERN_DEBUG, "Is a symlink; returning\n"); + goto out; + } + if (!nd) { + ecryptfs_printk(KERN_DEBUG, "We have a NULL nd, just leave" + "as we *think* we are about to unlink\n"); + goto out; + } + tlower_dentry = dget(lower_dentry); + if (!tlower_dentry || IS_ERR(tlower_dentry)) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Cannot dget lower_dentry\n"); + goto out_dput; + } + /* Released in this function */ + page_virt = + (char *)kmem_cache_alloc(ecryptfs_header_cache_2, + SLAB_USER); + if (!page_virt) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, + "Cannot ecryptfs_kmalloc a page\n"); + goto out_dput; + } + memset(page_virt, 0, PAGE_CACHE_SIZE); + rc = ecryptfs_read_header_region(page_virt, tlower_dentry, nd->mnt); + crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; + if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) + ecryptfs_set_default_sizes(crypt_stat); + if (rc) { + rc = 0; + ecryptfs_printk(KERN_WARNING, "Error reading header region;" + " assuming unencrypted\n"); + } else { + if (!contains_ecryptfs_marker(page_virt + + ECRYPTFS_FILE_SIZE_BYTES)) { + kmem_cache_free(ecryptfs_header_cache_2, page_virt); + goto out; + } + memcpy(&file_size, page_virt, sizeof(file_size)); + file_size = be64_to_cpu(file_size); + i_size_write(dentry->d_inode, (loff_t)file_size); + } + kmem_cache_free(ecryptfs_header_cache_2, page_virt); + goto out; + +out_dput: + dput(lower_dentry); + if (tlower_dentry) + dput(tlower_dentry); +out_drop: + d_drop(dentry); +out: + return ERR_PTR(rc); +} + +static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_dir_dentry; + u64 file_size_save; + int rc; + + file_size_save = i_size_read(old_dentry->d_inode); + lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); + lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); + dget(lower_old_dentry); + dget(lower_new_dentry); + lower_dir_dentry = lock_parent(lower_new_dentry); + rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, + lower_new_dentry); + if (rc || !lower_new_dentry->d_inode) + goto out_lock; + rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); + if (rc) + goto out_lock; + ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode); + old_dentry->d_inode->i_nlink = + ecryptfs_inode_to_lower(old_dentry->d_inode)->i_nlink; + i_size_write(new_dentry->d_inode, file_size_save); +out_lock: + unlock_dir(lower_dir_dentry); + dput(lower_new_dentry); + dput(lower_old_dentry); + if (!new_dentry->d_inode) + d_drop(new_dentry); + return rc; +} + +static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int rc = 0; + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); + + lock_parent(lower_dentry); + rc = vfs_unlink(lower_dir_inode, lower_dentry); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error in vfs_unlink\n"); + goto out_unlock; + } + ecryptfs_copy_attr_times(dir, lower_dir_inode); + dentry->d_inode->i_nlink = + ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink; + dentry->d_inode->i_ctime = dir->i_ctime; +out_unlock: + unlock_parent(lower_dentry); + return rc; +} + +static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int rc; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + umode_t mode; + char *encoded_symname; + unsigned int encoded_symlen; + struct ecryptfs_crypt_stat *crypt_stat = NULL; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + dget(lower_dentry); + lower_dir_dentry = lock_parent(lower_dentry); + mode = S_IALLUGO; + encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, + strlen(symname), + &encoded_symname); + if (encoded_symlen < 0) { + rc = encoded_symlen; + goto out_lock; + } + rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, + encoded_symname, mode); + kfree(encoded_symname); + if (rc || !lower_dentry->d_inode) + goto out_lock; + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + if (rc) + goto out_lock; + ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); +out_lock: + unlock_dir(lower_dir_dentry); + dput(lower_dentry); + if (!dentry->d_inode) + d_drop(dentry); + return rc; +} + +static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int rc; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + lower_dir_dentry = lock_parent(lower_dentry); + rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); + if (rc || !lower_dentry->d_inode) + goto out; + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + if (rc) + goto out; + ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); + dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; +out: + unlock_dir(lower_dir_dentry); + if (!dentry->d_inode) + d_drop(dentry); + return rc; +} + +static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int rc = 0; + struct dentry *tdentry = NULL; + struct dentry *lower_dentry; + struct dentry *tlower_dentry = NULL; + struct dentry *lower_dir_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!(tdentry = dget(dentry))) { + rc = -EINVAL; + ecryptfs_printk(KERN_ERR, "Error dget'ing dentry [%p]\n", + dentry); + goto out; + } + lower_dir_dentry = lock_parent(lower_dentry); + if (!(tlower_dentry = dget(lower_dentry))) { + rc = -EINVAL; + ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry " + "[%p]\n", lower_dentry); + goto out; + } + rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + if (!rc) { + d_delete(tlower_dentry); + tlower_dentry = NULL; + } + ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode); + dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; + unlock_dir(lower_dir_dentry); + if (!rc) + d_drop(dentry); +out: + if (tdentry) + dput(tdentry); + if (tlower_dentry) + dput(tlower_dentry); + return rc; +} + +static int +ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) +{ + int rc; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + lower_dir_dentry = lock_parent(lower_dentry); + rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev); + if (rc || !lower_dentry->d_inode) + goto out; + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + if (rc) + goto out; + ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); +out: + unlock_dir(lower_dir_dentry); + if (!dentry->d_inode) + d_drop(dentry); + return rc; +} + +static int +ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int rc; + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_old_dir_dentry; + struct dentry *lower_new_dir_dentry; + + lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); + lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); + dget(lower_old_dentry); + dget(lower_new_dentry); + lower_old_dir_dentry = dget_parent(lower_old_dentry); + lower_new_dir_dentry = dget_parent(lower_new_dentry); + lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, + lower_new_dir_dentry->d_inode, lower_new_dentry); + if (rc) + goto out_lock; + ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); + if (new_dir != old_dir) + ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); +out_lock: + unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + dput(lower_new_dentry); + dput(lower_old_dentry); + return rc; +} + +static int +ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz) +{ + int rc; + struct dentry *lower_dentry; + char *decoded_name; + char *lower_buf; + mm_segment_t old_fs; + struct ecryptfs_crypt_stat *crypt_stat; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!lower_dentry->d_inode->i_op || + !lower_dentry->d_inode->i_op->readlink) { + rc = -EINVAL; + goto out; + } + /* Released in this function */ + lower_buf = kmalloc(bufsiz, GFP_KERNEL); + if (lower_buf == NULL) { + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto out; + } + old_fs = get_fs(); + set_fs(get_ds()); + ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ " + "lower_dentry->d_name.name = [%s]\n", + lower_dentry->d_name.name); + rc = lower_dentry->d_inode->i_op->readlink(lower_dentry, + (char __user *)lower_buf, + bufsiz); + set_fs(old_fs); + if (rc >= 0) { + crypt_stat = NULL; + rc = ecryptfs_decode_filename(crypt_stat, lower_buf, rc, + &decoded_name); + if (rc == -ENOMEM) + goto out_free_lower_buf; + if (rc > 0) { + ecryptfs_printk(KERN_DEBUG, "Copying [%d] bytes " + "to userspace: [%*s]\n", rc, + decoded_name); + if (copy_to_user(buf, decoded_name, rc)) + rc = -EFAULT; + } + kfree(decoded_name); + ecryptfs_copy_attr_atime(dentry->d_inode, + lower_dentry->d_inode); + } +out_free_lower_buf: + kfree(lower_buf); +out: + return rc; +} + +static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *buf; + int len = PAGE_SIZE, rc; + mm_segment_t old_fs; + + /* Released in ecryptfs_put_link(); only release here on error */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + rc = -ENOMEM; + goto out; + } + old_fs = get_fs(); + set_fs(get_ds()); + ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ " + "dentry->d_name.name = [%s]\n", dentry->d_name.name); + rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len); + buf[rc] = '\0'; + set_fs(old_fs); + if (rc < 0) + goto out_free; + rc = 0; + nd_set_link(nd, buf); + goto out; +out_free: + kfree(buf); +out: + return ERR_PTR(rc); +} + +static void +ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr) +{ + /* Free the char* */ + kfree(nd_get_link(nd)); +} + +/** + * upper_size_to_lower_size + * @crypt_stat: Crypt_stat associated with file + * @upper_size: Size of the upper file + * + * Calculate the requried size of the lower file based on the + * specified size of the upper file. This calculation is based on the + * number of headers in the underlying file and the extent size. + * + * Returns Calculated size of the lower file. + */ +static loff_t +upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, + loff_t upper_size) +{ + loff_t lower_size; + + lower_size = ( crypt_stat->header_extent_size + * crypt_stat->num_header_extents_at_front ); + if (upper_size != 0) { + loff_t num_extents; + + num_extents = upper_size >> crypt_stat->extent_shift; + if (upper_size & ~crypt_stat->extent_mask) + num_extents++; + lower_size += (num_extents * crypt_stat->extent_size); + } + return lower_size; +} + +/** + * ecryptfs_truncate + * @dentry: The ecryptfs layer dentry + * @new_length: The length to expand the file to + * + * Function to handle truncations modifying the size of the file. Note + * that the file sizes are interpolated. When expanding, we are simply + * writing strings of 0's out. When truncating, we need to modify the + * underlying file size according to the page index interpolations. + * + * Returns zero on success; non-zero otherwise + */ +int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) +{ + int rc = 0; + struct inode *inode = dentry->d_inode; + struct dentry *lower_dentry; + struct vfsmount *lower_mnt; + struct file fake_ecryptfs_file, *lower_file = NULL; + struct ecryptfs_crypt_stat *crypt_stat; + loff_t i_size = i_size_read(inode); + loff_t lower_size_before_truncate; + loff_t lower_size_after_truncate; + + if (unlikely((new_length == i_size))) + goto out; + crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; + /* Set up a fake ecryptfs file, this is used to interface with + * the file in the underlying filesystem so that the + * truncation has an effect there as well. */ + memset(&fake_ecryptfs_file, 0, sizeof(fake_ecryptfs_file)); + fake_ecryptfs_file.f_dentry = dentry; + /* Released at out_free: label */ + ecryptfs_set_file_private(&fake_ecryptfs_file, + kmem_cache_alloc(ecryptfs_file_info_cache, + SLAB_KERNEL)); + if (unlikely(!ecryptfs_file_to_private(&fake_ecryptfs_file))) { + rc = -ENOMEM; + goto out; + } + lower_dentry = ecryptfs_dentry_to_lower(dentry); + /* This dget & mntget is released through fput at out_fput: */ + dget(lower_dentry); + lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + mntget(lower_mnt); + lower_file = dentry_open(lower_dentry, lower_mnt, O_RDWR); + if (unlikely(IS_ERR(lower_file))) { + rc = PTR_ERR(lower_file); + goto out_free; + } + ecryptfs_set_file_lower(&fake_ecryptfs_file, lower_file); + /* Switch on growing or shrinking file */ + if (new_length > i_size) { + rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length); + if (rc) { + ecryptfs_printk(KERN_ERR, + "Problem with fill_zeros\n"); + goto out_fput; + } + i_size_write(inode, new_length); + rc = ecryptfs_write_inode_size_to_header(lower_file, + lower_dentry->d_inode, + inode); + if (rc) { + ecryptfs_printk(KERN_ERR, + "Problem with ecryptfs_write" + "_inode_size\n"); + goto out_fput; + } + } else { /* new_length < i_size_read(inode) */ + vmtruncate(inode, new_length); + ecryptfs_write_inode_size_to_header(lower_file, + lower_dentry->d_inode, + inode); + /* We are reducing the size of the ecryptfs file, and need to + * know if we need to reduce the size of the lower file. */ + lower_size_before_truncate = + upper_size_to_lower_size(crypt_stat, i_size); + lower_size_after_truncate = + upper_size_to_lower_size(crypt_stat, new_length); + if (lower_size_after_truncate < lower_size_before_truncate) + vmtruncate(lower_dentry->d_inode, + lower_size_after_truncate); + } + /* Update the access times */ + lower_dentry->d_inode->i_mtime = lower_dentry->d_inode->i_ctime + = CURRENT_TIME; + mark_inode_dirty_sync(inode); +out_fput: + fput(lower_file); +out_free: + if (ecryptfs_file_to_private(&fake_ecryptfs_file)) + kmem_cache_free(ecryptfs_file_info_cache, + ecryptfs_file_to_private(&fake_ecryptfs_file)); +out: + return rc; +} + +static int +ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + int rc; + + if (nd) { + struct vfsmount *vfsmnt_save = nd->mnt; + struct dentry *dentry_save = nd->dentry; + + nd->mnt = ecryptfs_dentry_to_lower_mnt(nd->dentry); + nd->dentry = ecryptfs_dentry_to_lower(nd->dentry); + rc = permission(ecryptfs_inode_to_lower(inode), mask, nd); + nd->mnt = vfsmnt_save; + nd->dentry = dentry_save; + } else + rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL); + return rc; +} + +/** + * ecryptfs_setattr + * @dentry: dentry handle to the inode to modify + * @ia: Structure with flags of what to change and values + * + * Updates the metadata of an inode. If the update is to the size + * i.e. truncation, then ecryptfs_truncate will handle the size modification + * of both the ecryptfs inode and the lower inode. + * + * All other metadata changes will be passed right to the lower filesystem, + * and we will just update our inode to look like the lower. + */ +static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int rc = 0; + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct ecryptfs_crypt_stat *crypt_stat; + + crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; + lower_dentry = ecryptfs_dentry_to_lower(dentry); + inode = dentry->d_inode; + lower_inode = ecryptfs_inode_to_lower(inode); + if (ia->ia_valid & ATTR_SIZE) { + ecryptfs_printk(KERN_DEBUG, + "ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n", + ia->ia_valid, ATTR_SIZE); + rc = ecryptfs_truncate(dentry, ia->ia_size); + /* ecryptfs_truncate handles resizing of the lower file */ + ia->ia_valid &= ~ATTR_SIZE; + ecryptfs_printk(KERN_DEBUG, "ia->ia_valid = [%x]\n", + ia->ia_valid); + if (rc < 0) + goto out; + } + rc = notify_change(lower_dentry, ia); +out: + ecryptfs_copy_attr_all(inode, lower_inode); + return rc; +} + +static int +ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags) +{ + int rc = 0; + struct dentry *lower_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!lower_dentry->d_inode->i_op->setxattr) { + rc = -ENOSYS; + goto out; + } + mutex_lock(&lower_dentry->d_inode->i_mutex); + rc = lower_dentry->d_inode->i_op->setxattr(lower_dentry, name, value, + size, flags); + mutex_unlock(&lower_dentry->d_inode->i_mutex); +out: + return rc; +} + +static ssize_t +ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value, + size_t size) +{ + int rc = 0; + struct dentry *lower_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!lower_dentry->d_inode->i_op->getxattr) { + rc = -ENOSYS; + goto out; + } + mutex_lock(&lower_dentry->d_inode->i_mutex); + rc = lower_dentry->d_inode->i_op->getxattr(lower_dentry, name, value, + size); + mutex_unlock(&lower_dentry->d_inode->i_mutex); +out: + return rc; +} + +static ssize_t +ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size) +{ + int rc = 0; + struct dentry *lower_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!lower_dentry->d_inode->i_op->listxattr) { + rc = -ENOSYS; + goto out; + } + mutex_lock(&lower_dentry->d_inode->i_mutex); + rc = lower_dentry->d_inode->i_op->listxattr(lower_dentry, list, size); + mutex_unlock(&lower_dentry->d_inode->i_mutex); +out: + return rc; +} + +static int ecryptfs_removexattr(struct dentry *dentry, const char *name) +{ + int rc = 0; + struct dentry *lower_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!lower_dentry->d_inode->i_op->removexattr) { + rc = -ENOSYS; + goto out; + } + mutex_lock(&lower_dentry->d_inode->i_mutex); + rc = lower_dentry->d_inode->i_op->removexattr(lower_dentry, name); + mutex_unlock(&lower_dentry->d_inode->i_mutex); +out: + return rc; +} + +int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode) +{ + if ((ecryptfs_inode_to_lower(inode) + == (struct inode *)candidate_lower_inode)) + return 1; + else + return 0; +} + +int ecryptfs_inode_set(struct inode *inode, void *lower_inode) +{ + ecryptfs_init_inode(inode, (struct inode *)lower_inode); + return 0; +} + +struct inode_operations ecryptfs_symlink_iops = { + .readlink = ecryptfs_readlink, + .follow_link = ecryptfs_follow_link, + .put_link = ecryptfs_put_link, + .permission = ecryptfs_permission, + .setattr = ecryptfs_setattr, + .setxattr = ecryptfs_setxattr, + .getxattr = ecryptfs_getxattr, + .listxattr = ecryptfs_listxattr, + .removexattr = ecryptfs_removexattr +}; + +struct inode_operations ecryptfs_dir_iops = { + .create = ecryptfs_create, + .lookup = ecryptfs_lookup, + .link = ecryptfs_link, + .unlink = ecryptfs_unlink, + .symlink = ecryptfs_symlink, + .mkdir = ecryptfs_mkdir, + .rmdir = ecryptfs_rmdir, + .mknod = ecryptfs_mknod, + .rename = ecryptfs_rename, + .permission = ecryptfs_permission, + .setattr = ecryptfs_setattr, + .setxattr = ecryptfs_setxattr, + .getxattr = ecryptfs_getxattr, + .listxattr = ecryptfs_listxattr, + .removexattr = ecryptfs_removexattr +}; + +struct inode_operations ecryptfs_main_iops = { + .permission = ecryptfs_permission, + .setattr = ecryptfs_setattr, + .setxattr = ecryptfs_setxattr, + .getxattr = ecryptfs_getxattr, + .listxattr = ecryptfs_listxattr, + .removexattr = ecryptfs_removexattr +}; diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c new file mode 100644 index 0000000000..ba454785a0 --- /dev/null +++ b/fs/ecryptfs/keystore.c @@ -0,0 +1,1061 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * In-kernel key management code. Includes functions to parse and + * write authentication token-related packets with the underlying + * file. + * + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * request_key returned an error instead of a valid key address; + * determine the type of error, make appropriate log entries, and + * return an error code. + */ +int process_request_key_err(long err_code) +{ + int rc = 0; + + switch (err_code) { + case ENOKEY: + ecryptfs_printk(KERN_WARNING, "No key\n"); + rc = -ENOENT; + break; + case EKEYEXPIRED: + ecryptfs_printk(KERN_WARNING, "Key expired\n"); + rc = -ETIME; + break; + case EKEYREVOKED: + ecryptfs_printk(KERN_WARNING, "Key revoked\n"); + rc = -EINVAL; + break; + default: + ecryptfs_printk(KERN_WARNING, "Unknown error code: " + "[0x%.16x]\n", err_code); + rc = -EINVAL; + } + return rc; +} + +static void wipe_auth_tok_list(struct list_head *auth_tok_list_head) +{ + struct list_head *walker; + struct ecryptfs_auth_tok_list_item *auth_tok_list_item; + + walker = auth_tok_list_head->next; + while (walker != auth_tok_list_head) { + auth_tok_list_item = + list_entry(walker, struct ecryptfs_auth_tok_list_item, + list); + walker = auth_tok_list_item->list.next; + memset(auth_tok_list_item, 0, + sizeof(struct ecryptfs_auth_tok_list_item)); + kmem_cache_free(ecryptfs_auth_tok_list_item_cache, + auth_tok_list_item); + } +} + +struct kmem_cache *ecryptfs_auth_tok_list_item_cache; + +/** + * parse_packet_length + * @data: Pointer to memory containing length at offset + * @size: This function writes the decoded size to this memory + * address; zero on error + * @length_size: The number of bytes occupied by the encoded length + * + * Returns Zero on success + */ +static int parse_packet_length(unsigned char *data, size_t *size, + size_t *length_size) +{ + int rc = 0; + + (*length_size) = 0; + (*size) = 0; + if (data[0] < 192) { + /* One-byte length */ + (*size) = data[0]; + (*length_size) = 1; + } else if (data[0] < 224) { + /* Two-byte length */ + (*size) = ((data[0] - 192) * 256); + (*size) += (data[1] + 192); + (*length_size) = 2; + } else if (data[0] == 255) { + /* Five-byte length; we're not supposed to see this */ + ecryptfs_printk(KERN_ERR, "Five-byte packet length not " + "supported\n"); + rc = -EINVAL; + goto out; + } else { + ecryptfs_printk(KERN_ERR, "Error parsing packet length\n"); + rc = -EINVAL; + goto out; + } +out: + return rc; +} + +/** + * write_packet_length + * @dest: The byte array target into which to write the + * length. Must have at least 5 bytes allocated. + * @size: The length to write. + * @packet_size_length: The number of bytes used to encode the + * packet length is written to this address. + * + * Returns zero on success; non-zero on error. + */ +static int write_packet_length(char *dest, size_t size, + size_t *packet_size_length) +{ + int rc = 0; + + if (size < 192) { + dest[0] = size; + (*packet_size_length) = 1; + } else if (size < 65536) { + dest[0] = (((size - 192) / 256) + 192); + dest[1] = ((size - 192) % 256); + (*packet_size_length) = 2; + } else { + rc = -EINVAL; + ecryptfs_printk(KERN_WARNING, + "Unsupported packet size: [%d]\n", size); + } + return rc; +} + +/** + * parse_tag_3_packet + * @crypt_stat: The cryptographic context to modify based on packet + * contents. + * @data: The raw bytes of the packet. + * @auth_tok_list: eCryptfs parses packets into authentication tokens; + * a new authentication token will be placed at the end + * of this list for this packet. + * @new_auth_tok: Pointer to a pointer to memory that this function + * allocates; sets the memory address of the pointer to + * NULL on error. This object is added to the + * auth_tok_list. + * @packet_size: This function writes the size of the parsed packet + * into this memory location; zero on error. + * @max_packet_size: maximum number of bytes to parse + * + * Returns zero on success; non-zero on error. + */ +static int +parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat, + unsigned char *data, struct list_head *auth_tok_list, + struct ecryptfs_auth_tok **new_auth_tok, + size_t *packet_size, size_t max_packet_size) +{ + int rc = 0; + size_t body_size; + struct ecryptfs_auth_tok_list_item *auth_tok_list_item; + size_t length_size; + + (*packet_size) = 0; + (*new_auth_tok) = NULL; + + /* we check that: + * one byte for the Tag 3 ID flag + * two bytes for the body size + * do not exceed the maximum_packet_size + */ + if (unlikely((*packet_size) + 3 > max_packet_size)) { + ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); + rc = -EINVAL; + goto out; + } + + /* check for Tag 3 identifyer - one byte */ + if (data[(*packet_size)++] != ECRYPTFS_TAG_3_PACKET_TYPE) { + ecryptfs_printk(KERN_ERR, "Enter w/ first byte != 0x%.2x\n", + ECRYPTFS_TAG_3_PACKET_TYPE); + rc = -EINVAL; + goto out; + } + /* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or + * at end of function upon failure */ + auth_tok_list_item = + kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache, SLAB_KERNEL); + if (!auth_tok_list_item) { + ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n"); + rc = -ENOMEM; + goto out; + } + memset(auth_tok_list_item, 0, + sizeof(struct ecryptfs_auth_tok_list_item)); + (*new_auth_tok) = &auth_tok_list_item->auth_tok; + + /* check for body size - one to two bytes */ + rc = parse_packet_length(&data[(*packet_size)], &body_size, + &length_size); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error parsing packet length; " + "rc = [%d]\n", rc); + goto out_free; + } + if (unlikely(body_size < (0x05 + ECRYPTFS_SALT_SIZE))) { + ecryptfs_printk(KERN_WARNING, "Invalid body size ([%d])\n", + body_size); + rc = -EINVAL; + goto out_free; + } + (*packet_size) += length_size; + + /* now we know the length of the remainting Tag 3 packet size: + * 5 fix bytes for: version string, cipher, S2K ID, hash algo, + * number of hash iterations + * ECRYPTFS_SALT_SIZE bytes for salt + * body_size bytes minus the stuff above is the encrypted key size + */ + if (unlikely((*packet_size) + body_size > max_packet_size)) { + ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); + rc = -EINVAL; + goto out_free; + } + + /* There are 5 characters of additional information in the + * packet */ + (*new_auth_tok)->session_key.encrypted_key_size = + body_size - (0x05 + ECRYPTFS_SALT_SIZE); + ecryptfs_printk(KERN_DEBUG, "Encrypted key size = [%d]\n", + (*new_auth_tok)->session_key.encrypted_key_size); + + /* Version 4 (from RFC2440) - one byte */ + if (unlikely(data[(*packet_size)++] != 0x04)) { + ecryptfs_printk(KERN_DEBUG, "Unknown version number " + "[%d]\n", data[(*packet_size) - 1]); + rc = -EINVAL; + goto out_free; + } + + /* cipher - one byte */ + ecryptfs_cipher_code_to_string(crypt_stat->cipher, + (u16)data[(*packet_size)]); + /* A little extra work to differentiate among the AES key + * sizes; see RFC2440 */ + switch(data[(*packet_size)++]) { + case RFC2440_CIPHER_AES_192: + crypt_stat->key_size = 24; + break; + default: + crypt_stat->key_size = + (*new_auth_tok)->session_key.encrypted_key_size; + } + ecryptfs_init_crypt_ctx(crypt_stat); + /* S2K identifier 3 (from RFC2440) */ + if (unlikely(data[(*packet_size)++] != 0x03)) { + ecryptfs_printk(KERN_ERR, "Only S2K ID 3 is currently " + "supported\n"); + rc = -ENOSYS; + goto out_free; + } + + /* TODO: finish the hash mapping */ + /* hash algorithm - one byte */ + switch (data[(*packet_size)++]) { + case 0x01: /* See RFC2440 for these numbers and their mappings */ + /* Choose MD5 */ + /* salt - ECRYPTFS_SALT_SIZE bytes */ + memcpy((*new_auth_tok)->token.password.salt, + &data[(*packet_size)], ECRYPTFS_SALT_SIZE); + (*packet_size) += ECRYPTFS_SALT_SIZE; + + /* This conversion was taken straight from RFC2440 */ + /* number of hash iterations - one byte */ + (*new_auth_tok)->token.password.hash_iterations = + ((u32) 16 + (data[(*packet_size)] & 15)) + << ((data[(*packet_size)] >> 4) + 6); + (*packet_size)++; + + /* encrypted session key - + * (body_size-5-ECRYPTFS_SALT_SIZE) bytes */ + memcpy((*new_auth_tok)->session_key.encrypted_key, + &data[(*packet_size)], + (*new_auth_tok)->session_key.encrypted_key_size); + (*packet_size) += + (*new_auth_tok)->session_key.encrypted_key_size; + (*new_auth_tok)->session_key.flags &= + ~ECRYPTFS_CONTAINS_DECRYPTED_KEY; + (*new_auth_tok)->session_key.flags |= + ECRYPTFS_CONTAINS_ENCRYPTED_KEY; + (*new_auth_tok)->token.password.hash_algo = 0x01; + break; + default: + ecryptfs_printk(KERN_ERR, "Unsupported hash algorithm: " + "[%d]\n", data[(*packet_size) - 1]); + rc = -ENOSYS; + goto out_free; + } + (*new_auth_tok)->token_type = ECRYPTFS_PASSWORD; + /* TODO: Parametarize; we might actually want userspace to + * decrypt the session key. */ + ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags, + ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT); + ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags, + ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT); + list_add(&auth_tok_list_item->list, auth_tok_list); + goto out; +out_free: + (*new_auth_tok) = NULL; + memset(auth_tok_list_item, 0, + sizeof(struct ecryptfs_auth_tok_list_item)); + kmem_cache_free(ecryptfs_auth_tok_list_item_cache, + auth_tok_list_item); +out: + if (rc) + (*packet_size) = 0; + return rc; +} + +/** + * parse_tag_11_packet + * @data: The raw bytes of the packet + * @contents: This function writes the data contents of the literal + * packet into this memory location + * @max_contents_bytes: The maximum number of bytes that this function + * is allowed to write into contents + * @tag_11_contents_size: This function writes the size of the parsed + * contents into this memory location; zero on + * error + * @packet_size: This function writes the size of the parsed packet + * into this memory location; zero on error + * @max_packet_size: maximum number of bytes to parse + * + * Returns zero on success; non-zero on error. + */ +static int +parse_tag_11_packet(unsigned char *data, unsigned char *contents, + size_t max_contents_bytes, size_t *tag_11_contents_size, + size_t *packet_size, size_t max_packet_size) +{ + int rc = 0; + size_t body_size; + size_t length_size; + + (*packet_size) = 0; + (*tag_11_contents_size) = 0; + + /* check that: + * one byte for the Tag 11 ID flag + * two bytes for the Tag 11 length + * do not exceed the maximum_packet_size + */ + if (unlikely((*packet_size) + 3 > max_packet_size)) { + ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); + rc = -EINVAL; + goto out; + } + + /* check for Tag 11 identifyer - one byte */ + if (data[(*packet_size)++] != ECRYPTFS_TAG_11_PACKET_TYPE) { + ecryptfs_printk(KERN_WARNING, + "Invalid tag 11 packet format\n"); + rc = -EINVAL; + goto out; + } + + /* get Tag 11 content length - one or two bytes */ + rc = parse_packet_length(&data[(*packet_size)], &body_size, + &length_size); + if (rc) { + ecryptfs_printk(KERN_WARNING, + "Invalid tag 11 packet format\n"); + goto out; + } + (*packet_size) += length_size; + + if (body_size < 13) { + ecryptfs_printk(KERN_WARNING, "Invalid body size ([%d])\n", + body_size); + rc = -EINVAL; + goto out; + } + /* We have 13 bytes of surrounding packet values */ + (*tag_11_contents_size) = (body_size - 13); + + /* now we know the length of the remainting Tag 11 packet size: + * 14 fix bytes for: special flag one, special flag two, + * 12 skipped bytes + * body_size bytes minus the stuff above is the Tag 11 content + */ + /* FIXME why is the body size one byte smaller than the actual + * size of the body? + * this seems to be an error here as well as in + * write_tag_11_packet() */ + if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) { + ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); + rc = -EINVAL; + goto out; + } + + /* special flag one - one byte */ + if (data[(*packet_size)++] != 0x62) { + ecryptfs_printk(KERN_WARNING, "Unrecognizable packet\n"); + rc = -EINVAL; + goto out; + } + + /* special flag two - one byte */ + if (data[(*packet_size)++] != 0x08) { + ecryptfs_printk(KERN_WARNING, "Unrecognizable packet\n"); + rc = -EINVAL; + goto out; + } + + /* skip the next 12 bytes */ + (*packet_size) += 12; /* We don't care about the filename or + * the timestamp */ + + /* get the Tag 11 contents - tag_11_contents_size bytes */ + memcpy(contents, &data[(*packet_size)], (*tag_11_contents_size)); + (*packet_size) += (*tag_11_contents_size); + +out: + if (rc) { + (*packet_size) = 0; + (*tag_11_contents_size) = 0; + } + return rc; +} + +/** + * decrypt_session_key - Decrypt the session key with the given auth_tok. + * + * Returns Zero on success; non-zero error otherwise. + */ +static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok, + struct ecryptfs_crypt_stat *crypt_stat) +{ + int rc = 0; + struct ecryptfs_password *password_s_ptr; + struct crypto_tfm *tfm = NULL; + struct scatterlist src_sg[2], dst_sg[2]; + struct mutex *tfm_mutex = NULL; + /* TODO: Use virt_to_scatterlist for these */ + char *encrypted_session_key; + char *session_key; + + password_s_ptr = &auth_tok->token.password; + if (ECRYPTFS_CHECK_FLAG(password_s_ptr->flags, + ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)) + ecryptfs_printk(KERN_DEBUG, "Session key encryption key " + "set; skipping key generation\n"); + ecryptfs_printk(KERN_DEBUG, "Session key encryption key (size [%d])" + ":\n", + password_s_ptr->session_key_encryption_key_bytes); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(password_s_ptr->session_key_encryption_key, + password_s_ptr-> + session_key_encryption_key_bytes); + if (!strcmp(crypt_stat->cipher, + crypt_stat->mount_crypt_stat->global_default_cipher_name) + && crypt_stat->mount_crypt_stat->global_key_tfm) { + tfm = crypt_stat->mount_crypt_stat->global_key_tfm; + tfm_mutex = &crypt_stat->mount_crypt_stat->global_key_tfm_mutex; + } else { + tfm = crypto_alloc_tfm(crypt_stat->cipher, + CRYPTO_TFM_REQ_WEAK_KEY); + if (!tfm) { + printk(KERN_ERR "Error allocating crypto context\n"); + rc = -ENOMEM; + goto out; + } + } + if (password_s_ptr->session_key_encryption_key_bytes + < crypto_tfm_alg_min_keysize(tfm)) { + printk(KERN_WARNING "Session key encryption key is [%d] bytes; " + "minimum keysize for selected cipher is [%d] bytes.\n", + password_s_ptr->session_key_encryption_key_bytes, + crypto_tfm_alg_min_keysize(tfm)); + rc = -EINVAL; + goto out; + } + if (tfm_mutex) + mutex_lock(tfm_mutex); + crypto_cipher_setkey(tfm, password_s_ptr->session_key_encryption_key, + crypt_stat->key_size); + /* TODO: virt_to_scatterlist */ + encrypted_session_key = (char *)__get_free_page(GFP_KERNEL); + if (!encrypted_session_key) { + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto out_free_tfm; + } + session_key = (char *)__get_free_page(GFP_KERNEL); + if (!session_key) { + kfree(encrypted_session_key); + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto out_free_tfm; + } + memcpy(encrypted_session_key, auth_tok->session_key.encrypted_key, + auth_tok->session_key.encrypted_key_size); + src_sg[0].page = virt_to_page(encrypted_session_key); + src_sg[0].offset = 0; + BUG_ON(auth_tok->session_key.encrypted_key_size > PAGE_CACHE_SIZE); + src_sg[0].length = auth_tok->session_key.encrypted_key_size; + dst_sg[0].page = virt_to_page(session_key); + dst_sg[0].offset = 0; + auth_tok->session_key.decrypted_key_size = + auth_tok->session_key.encrypted_key_size; + dst_sg[0].length = auth_tok->session_key.encrypted_key_size; + /* TODO: Handle error condition */ + crypto_cipher_decrypt(tfm, dst_sg, src_sg, + auth_tok->session_key.encrypted_key_size); + auth_tok->session_key.decrypted_key_size = + auth_tok->session_key.encrypted_key_size; + memcpy(auth_tok->session_key.decrypted_key, session_key, + auth_tok->session_key.decrypted_key_size); + auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY; + memcpy(crypt_stat->key, auth_tok->session_key.decrypted_key, + auth_tok->session_key.decrypted_key_size); + ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); + ecryptfs_printk(KERN_DEBUG, "Decrypted session key:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(crypt_stat->key, + crypt_stat->key_size); + memset(encrypted_session_key, 0, PAGE_CACHE_SIZE); + free_page((unsigned long)encrypted_session_key); + memset(session_key, 0, PAGE_CACHE_SIZE); + free_page((unsigned long)session_key); +out_free_tfm: + if (tfm_mutex) + mutex_unlock(tfm_mutex); + else + crypto_free_tfm(tfm); +out: + return rc; +} + +/** + * ecryptfs_parse_packet_set + * @dest: The header page in memory + * @version: Version of file format, to guide parsing behavior + * + * Get crypt_stat to have the file's session key if the requisite key + * is available to decrypt the session key. + * + * Returns Zero if a valid authentication token was retrieved and + * processed; negative value for file not encrypted or for error + * conditions. + */ +int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, + unsigned char *src, + struct dentry *ecryptfs_dentry) +{ + size_t i = 0; + int rc = 0; + size_t found_auth_tok = 0; + size_t next_packet_is_auth_tok_packet; + char sig[ECRYPTFS_SIG_SIZE_HEX]; + struct list_head auth_tok_list; + struct list_head *walker; + struct ecryptfs_auth_tok *chosen_auth_tok = NULL; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat = + &ecryptfs_superblock_to_private( + ecryptfs_dentry->d_sb)->mount_crypt_stat; + struct ecryptfs_auth_tok *candidate_auth_tok = NULL; + size_t packet_size; + struct ecryptfs_auth_tok *new_auth_tok; + unsigned char sig_tmp_space[ECRYPTFS_SIG_SIZE]; + size_t tag_11_contents_size; + size_t tag_11_packet_size; + + INIT_LIST_HEAD(&auth_tok_list); + /* Parse the header to find as many packets as we can, these will be + * added the our &auth_tok_list */ + next_packet_is_auth_tok_packet = 1; + while (next_packet_is_auth_tok_packet) { + size_t max_packet_size = ((PAGE_CACHE_SIZE - 8) - i); + + switch (src[i]) { + case ECRYPTFS_TAG_3_PACKET_TYPE: + rc = parse_tag_3_packet(crypt_stat, + (unsigned char *)&src[i], + &auth_tok_list, &new_auth_tok, + &packet_size, max_packet_size); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error parsing " + "tag 3 packet\n"); + rc = -EIO; + goto out_wipe_list; + } + i += packet_size; + rc = parse_tag_11_packet((unsigned char *)&src[i], + sig_tmp_space, + ECRYPTFS_SIG_SIZE, + &tag_11_contents_size, + &tag_11_packet_size, + max_packet_size); + if (rc) { + ecryptfs_printk(KERN_ERR, "No valid " + "(ecryptfs-specific) literal " + "packet containing " + "authentication token " + "signature found after " + "tag 3 packet\n"); + rc = -EIO; + goto out_wipe_list; + } + i += tag_11_packet_size; + if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) { + ecryptfs_printk(KERN_ERR, "Expected " + "signature of size [%d]; " + "read size [%d]\n", + ECRYPTFS_SIG_SIZE, + tag_11_contents_size); + rc = -EIO; + goto out_wipe_list; + } + ecryptfs_to_hex(new_auth_tok->token.password.signature, + sig_tmp_space, tag_11_contents_size); + new_auth_tok->token.password.signature[ + ECRYPTFS_PASSWORD_SIG_SIZE] = '\0'; + ECRYPTFS_SET_FLAG(crypt_stat->flags, + ECRYPTFS_ENCRYPTED); + break; + case ECRYPTFS_TAG_11_PACKET_TYPE: + ecryptfs_printk(KERN_WARNING, "Invalid packet set " + "(Tag 11 not allowed by itself)\n"); + rc = -EIO; + goto out_wipe_list; + break; + default: + ecryptfs_printk(KERN_DEBUG, "No packet at offset " + "[%d] of the file header; hex value of " + "character is [0x%.2x]\n", i, src[i]); + next_packet_is_auth_tok_packet = 0; + } + } + if (list_empty(&auth_tok_list)) { + rc = -EINVAL; /* Do not support non-encrypted files in + * the 0.1 release */ + goto out; + } + /* If we have a global auth tok, then we should try to use + * it */ + if (mount_crypt_stat->global_auth_tok) { + memcpy(sig, mount_crypt_stat->global_auth_tok_sig, + ECRYPTFS_SIG_SIZE_HEX); + chosen_auth_tok = mount_crypt_stat->global_auth_tok; + } else + BUG(); /* We should always have a global auth tok in + * the 0.1 release */ + /* Scan list to see if our chosen_auth_tok works */ + list_for_each(walker, &auth_tok_list) { + struct ecryptfs_auth_tok_list_item *auth_tok_list_item; + auth_tok_list_item = + list_entry(walker, struct ecryptfs_auth_tok_list_item, + list); + candidate_auth_tok = &auth_tok_list_item->auth_tok; + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, + "Considering cadidate auth tok:\n"); + ecryptfs_dump_auth_tok(candidate_auth_tok); + } + /* TODO: Replace ECRYPTFS_SIG_SIZE_HEX w/ dynamic value */ + if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD + && !strncmp(candidate_auth_tok->token.password.signature, + sig, ECRYPTFS_SIG_SIZE_HEX)) { + found_auth_tok = 1; + goto leave_list; + /* TODO: Transfer the common salt into the + * crypt_stat salt */ + } + } +leave_list: + if (!found_auth_tok) { + ecryptfs_printk(KERN_ERR, "Could not find authentication " + "token on temporary list for sig [%.*s]\n", + ECRYPTFS_SIG_SIZE_HEX, sig); + rc = -EIO; + goto out_wipe_list; + } else { + memcpy(&(candidate_auth_tok->token.password), + &(chosen_auth_tok->token.password), + sizeof(struct ecryptfs_password)); + rc = decrypt_session_key(candidate_auth_tok, crypt_stat); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error decrypting the " + "session key\n"); + goto out_wipe_list; + } + rc = ecryptfs_compute_root_iv(crypt_stat); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error computing " + "the root IV\n"); + goto out_wipe_list; + } + } + rc = ecryptfs_init_crypt_ctx(crypt_stat); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error initializing crypto " + "context for cipher [%s]; rc = [%d]\n", + crypt_stat->cipher, rc); + } +out_wipe_list: + wipe_auth_tok_list(&auth_tok_list); +out: + return rc; +} + +/** + * write_tag_11_packet + * @dest: Target into which Tag 11 packet is to be written + * @max: Maximum packet length + * @contents: Byte array of contents to copy in + * @contents_length: Number of bytes in contents + * @packet_length: Length of the Tag 11 packet written; zero on error + * + * Returns zero on success; non-zero on error. + */ +static int +write_tag_11_packet(char *dest, int max, char *contents, size_t contents_length, + size_t *packet_length) +{ + int rc = 0; + size_t packet_size_length; + + (*packet_length) = 0; + if ((13 + contents_length) > max) { + rc = -EINVAL; + ecryptfs_printk(KERN_ERR, "Packet length larger than " + "maximum allowable\n"); + goto out; + } + /* General packet header */ + /* Packet tag */ + dest[(*packet_length)++] = ECRYPTFS_TAG_11_PACKET_TYPE; + /* Packet length */ + rc = write_packet_length(&dest[(*packet_length)], + (13 + contents_length), &packet_size_length); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error generating tag 11 packet " + "header; cannot generate packet length\n"); + goto out; + } + (*packet_length) += packet_size_length; + /* Tag 11 specific */ + /* One-octet field that describes how the data is formatted */ + dest[(*packet_length)++] = 0x62; /* binary data */ + /* One-octet filename length followed by filename */ + dest[(*packet_length)++] = 8; + memcpy(&dest[(*packet_length)], "_CONSOLE", 8); + (*packet_length) += 8; + /* Four-octet number indicating modification date */ + memset(&dest[(*packet_length)], 0x00, 4); + (*packet_length) += 4; + /* Remainder is literal data */ + memcpy(&dest[(*packet_length)], contents, contents_length); + (*packet_length) += contents_length; + out: + if (rc) + (*packet_length) = 0; + return rc; +} + +/** + * write_tag_3_packet + * @dest: Buffer into which to write the packet + * @max: Maximum number of bytes that can be written + * @auth_tok: Authentication token + * @crypt_stat: The cryptographic context + * @key_rec: encrypted key + * @packet_size: This function will write the number of bytes that end + * up constituting the packet; set to zero on error + * + * Returns zero on success; non-zero on error. + */ +static int +write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok, + struct ecryptfs_crypt_stat *crypt_stat, + struct ecryptfs_key_record *key_rec, size_t *packet_size) +{ + int rc = 0; + + size_t i; + size_t signature_is_valid = 0; + size_t encrypted_session_key_valid = 0; + char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; + struct scatterlist dest_sg[2]; + struct scatterlist src_sg[2]; + struct crypto_tfm *tfm = NULL; + struct mutex *tfm_mutex = NULL; + size_t key_rec_size; + size_t packet_size_length; + size_t cipher_code; + + (*packet_size) = 0; + /* Check for a valid signature on the auth_tok */ + for (i = 0; i < ECRYPTFS_SIG_SIZE_HEX; i++) + signature_is_valid |= auth_tok->token.password.signature[i]; + if (!signature_is_valid) + BUG(); + ecryptfs_from_hex((*key_rec).sig, auth_tok->token.password.signature, + ECRYPTFS_SIG_SIZE); + encrypted_session_key_valid = 0; + for (i = 0; i < crypt_stat->key_size; i++) + encrypted_session_key_valid |= + auth_tok->session_key.encrypted_key[i]; + if (encrypted_session_key_valid) { + memcpy((*key_rec).enc_key, + auth_tok->session_key.encrypted_key, + auth_tok->session_key.encrypted_key_size); + goto encrypted_session_key_set; + } + if (auth_tok->session_key.encrypted_key_size == 0) + auth_tok->session_key.encrypted_key_size = + crypt_stat->key_size; + if (crypt_stat->key_size == 24 + && strcmp("aes", crypt_stat->cipher) == 0) { + memset((crypt_stat->key + 24), 0, 8); + auth_tok->session_key.encrypted_key_size = 32; + } + (*key_rec).enc_key_size = + auth_tok->session_key.encrypted_key_size; + if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags, + ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)) { + ecryptfs_printk(KERN_DEBUG, "Using previously generated " + "session key encryption key of size [%d]\n", + auth_tok->token.password. + session_key_encryption_key_bytes); + memcpy(session_key_encryption_key, + auth_tok->token.password.session_key_encryption_key, + crypt_stat->key_size); + ecryptfs_printk(KERN_DEBUG, + "Cached session key " "encryption key: \n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(session_key_encryption_key, 16); + } + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(KERN_DEBUG, "Session key encryption key:\n"); + ecryptfs_dump_hex(session_key_encryption_key, 16); + } + rc = virt_to_scatterlist(crypt_stat->key, + (*key_rec).enc_key_size, src_sg, 2); + if (!rc) { + ecryptfs_printk(KERN_ERR, "Error generating scatterlist " + "for crypt_stat session key\n"); + rc = -ENOMEM; + goto out; + } + rc = virt_to_scatterlist((*key_rec).enc_key, + (*key_rec).enc_key_size, dest_sg, 2); + if (!rc) { + ecryptfs_printk(KERN_ERR, "Error generating scatterlist " + "for crypt_stat encrypted session key\n"); + rc = -ENOMEM; + goto out; + } + if (!strcmp(crypt_stat->cipher, + crypt_stat->mount_crypt_stat->global_default_cipher_name) + && crypt_stat->mount_crypt_stat->global_key_tfm) { + tfm = crypt_stat->mount_crypt_stat->global_key_tfm; + tfm_mutex = &crypt_stat->mount_crypt_stat->global_key_tfm_mutex; + } else + tfm = crypto_alloc_tfm(crypt_stat->cipher, 0); + if (!tfm) { + ecryptfs_printk(KERN_ERR, "Could not initialize crypto " + "context for cipher [%s]\n", + crypt_stat->cipher); + rc = -EINVAL; + goto out; + } + if (tfm_mutex) + mutex_lock(tfm_mutex); + rc = crypto_cipher_setkey(tfm, session_key_encryption_key, + crypt_stat->key_size); + if (rc < 0) { + if (tfm_mutex) + mutex_unlock(tfm_mutex); + ecryptfs_printk(KERN_ERR, "Error setting key for crypto " + "context\n"); + goto out; + } + rc = 0; + ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes of the key\n", + crypt_stat->key_size); + crypto_cipher_encrypt(tfm, dest_sg, src_sg, + (*key_rec).enc_key_size); + if (tfm_mutex) + mutex_unlock(tfm_mutex); + ecryptfs_printk(KERN_DEBUG, "This should be the encrypted key:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex((*key_rec).enc_key, + (*key_rec).enc_key_size); +encrypted_session_key_set: + /* Now we have a valid key_rec. Append it to the + * key_rec set. */ + key_rec_size = (sizeof(struct ecryptfs_key_record) + - ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES + + ((*key_rec).enc_key_size)); + /* TODO: Include a packet size limit as a parameter to this + * function once we have multi-packet headers (for versions + * later than 0.1 */ + if (key_rec_size >= ECRYPTFS_MAX_KEYSET_SIZE) { + ecryptfs_printk(KERN_ERR, "Keyset too large\n"); + rc = -EINVAL; + goto out; + } + /* TODO: Packet size limit */ + /* We have 5 bytes of surrounding packet data */ + if ((0x05 + ECRYPTFS_SALT_SIZE + + (*key_rec).enc_key_size) >= max) { + ecryptfs_printk(KERN_ERR, "Authentication token is too " + "large\n"); + rc = -EINVAL; + goto out; + } + /* This format is inspired by OpenPGP; see RFC 2440 + * packet tag 3 */ + dest[(*packet_size)++] = ECRYPTFS_TAG_3_PACKET_TYPE; + /* ver+cipher+s2k+hash+salt+iter+enc_key */ + rc = write_packet_length(&dest[(*packet_size)], + (0x05 + ECRYPTFS_SALT_SIZE + + (*key_rec).enc_key_size), + &packet_size_length); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error generating tag 3 packet " + "header; cannot generate packet length\n"); + goto out; + } + (*packet_size) += packet_size_length; + dest[(*packet_size)++] = 0x04; /* version 4 */ + cipher_code = ecryptfs_code_for_cipher_string(crypt_stat); + if (cipher_code == 0) { + ecryptfs_printk(KERN_WARNING, "Unable to generate code for " + "cipher [%s]\n", crypt_stat->cipher); + rc = -EINVAL; + goto out; + } + dest[(*packet_size)++] = cipher_code; + dest[(*packet_size)++] = 0x03; /* S2K */ + dest[(*packet_size)++] = 0x01; /* MD5 (TODO: parameterize) */ + memcpy(&dest[(*packet_size)], auth_tok->token.password.salt, + ECRYPTFS_SALT_SIZE); + (*packet_size) += ECRYPTFS_SALT_SIZE; /* salt */ + dest[(*packet_size)++] = 0x60; /* hash iterations (65536) */ + memcpy(&dest[(*packet_size)], (*key_rec).enc_key, + (*key_rec).enc_key_size); + (*packet_size) += (*key_rec).enc_key_size; +out: + if (tfm && !tfm_mutex) + crypto_free_tfm(tfm); + if (rc) + (*packet_size) = 0; + return rc; +} + +/** + * ecryptfs_generate_key_packet_set + * @dest: Virtual address from which to write the key record set + * @crypt_stat: The cryptographic context from which the + * authentication tokens will be retrieved + * @ecryptfs_dentry: The dentry, used to retrieve the mount crypt stat + * for the global parameters + * @len: The amount written + * @max: The maximum amount of data allowed to be written + * + * Generates a key packet set and writes it to the virtual address + * passed in. + * + * Returns zero on success; non-zero on error. + */ +int +ecryptfs_generate_key_packet_set(char *dest_base, + struct ecryptfs_crypt_stat *crypt_stat, + struct dentry *ecryptfs_dentry, size_t *len, + size_t max) +{ + int rc = 0; + struct ecryptfs_auth_tok *auth_tok; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat = + &ecryptfs_superblock_to_private( + ecryptfs_dentry->d_sb)->mount_crypt_stat; + size_t written; + struct ecryptfs_key_record key_rec; + + (*len) = 0; + if (mount_crypt_stat->global_auth_tok) { + auth_tok = mount_crypt_stat->global_auth_tok; + if (auth_tok->token_type == ECRYPTFS_PASSWORD) { + rc = write_tag_3_packet((dest_base + (*len)), + max, auth_tok, + crypt_stat, &key_rec, + &written); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error " + "writing tag 3 packet\n"); + goto out; + } + (*len) += written; + /* Write auth tok signature packet */ + rc = write_tag_11_packet( + (dest_base + (*len)), + (max - (*len)), + key_rec.sig, ECRYPTFS_SIG_SIZE, &written); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error writing " + "auth tok signature packet\n"); + goto out; + } + (*len) += written; + } else { + ecryptfs_printk(KERN_WARNING, "Unsupported " + "authentication token type\n"); + rc = -EINVAL; + goto out; + } + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error writing " + "authentication token packet with sig " + "= [%s]\n", + mount_crypt_stat->global_auth_tok_sig); + rc = -EIO; + goto out; + } + } else + BUG(); + if (likely((max - (*len)) > 0)) { + dest_base[(*len)] = 0x00; + } else { + ecryptfs_printk(KERN_ERR, "Error writing boundary byte\n"); + rc = -EIO; + } +out: + if (rc) + (*len) = 0; + return rc; +} diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c new file mode 100644 index 0000000000..7a11b8ae66 --- /dev/null +++ b/fs/ecryptfs/main.c @@ -0,0 +1,831 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2003 Erez Zadok + * Copyright (C) 2001-2003 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * Module parameter that defines the ecryptfs_verbosity level. + */ +int ecryptfs_verbosity = 0; + +module_param(ecryptfs_verbosity, int, 0); +MODULE_PARM_DESC(ecryptfs_verbosity, + "Initial verbosity level (0 or 1; defaults to " + "0, which is Quiet)"); + +void __ecryptfs_printk(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (fmt[1] == '7') { /* KERN_DEBUG */ + if (ecryptfs_verbosity >= 1) + vprintk(fmt, args); + } else + vprintk(fmt, args); + va_end(args); +} + +/** + * ecryptfs_interpose + * @lower_dentry: Existing dentry in the lower filesystem + * @dentry: ecryptfs' dentry + * @sb: ecryptfs's super_block + * @flag: If set to true, then d_add is called, else d_instantiate is called + * + * Interposes upper and lower dentries. + * + * Returns zero on success; non-zero otherwise + */ +int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry, + struct super_block *sb, int flag) +{ + struct inode *lower_inode; + struct inode *inode; + int rc = 0; + + lower_inode = lower_dentry->d_inode; + if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) { + rc = -EXDEV; + goto out; + } + if (!igrab(lower_inode)) { + rc = -ESTALE; + goto out; + } + inode = iget5_locked(sb, (unsigned long)lower_inode, + ecryptfs_inode_test, ecryptfs_inode_set, + lower_inode); + if (!inode) { + rc = -EACCES; + iput(lower_inode); + goto out; + } + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + else + iput(lower_inode); + if (S_ISLNK(lower_inode->i_mode)) + inode->i_op = &ecryptfs_symlink_iops; + else if (S_ISDIR(lower_inode->i_mode)) + inode->i_op = &ecryptfs_dir_iops; + if (S_ISDIR(lower_inode->i_mode)) + inode->i_fop = &ecryptfs_dir_fops; + /* TODO: Is there a better way to identify if the inode is + * special? */ + if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || + S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) + init_special_inode(inode, lower_inode->i_mode, + lower_inode->i_rdev); + dentry->d_op = &ecryptfs_dops; + if (flag) + d_add(dentry, inode); + else + d_instantiate(dentry, inode); + ecryptfs_copy_attr_all(inode, lower_inode); + /* This size will be overwritten for real files w/ headers and + * other metadata */ + ecryptfs_copy_inode_size(inode, lower_inode); +out: + return rc; +} + +enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_debug, + ecryptfs_opt_ecryptfs_debug, ecryptfs_opt_cipher, + ecryptfs_opt_ecryptfs_cipher, ecryptfs_opt_ecryptfs_key_bytes, + ecryptfs_opt_passthrough, ecryptfs_opt_err }; + +static match_table_t tokens = { + {ecryptfs_opt_sig, "sig=%s"}, + {ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"}, + {ecryptfs_opt_debug, "debug=%u"}, + {ecryptfs_opt_ecryptfs_debug, "ecryptfs_debug=%u"}, + {ecryptfs_opt_cipher, "cipher=%s"}, + {ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"}, + {ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"}, + {ecryptfs_opt_passthrough, "ecryptfs_passthrough"}, + {ecryptfs_opt_err, NULL} +}; + +/** + * ecryptfs_verify_version + * @version: The version number to confirm + * + * Returns zero on good version; non-zero otherwise + */ +static int ecryptfs_verify_version(u16 version) +{ + int rc = 0; + unsigned char major; + unsigned char minor; + + major = ((version >> 8) & 0xFF); + minor = (version & 0xFF); + if (major != ECRYPTFS_VERSION_MAJOR) { + ecryptfs_printk(KERN_ERR, "Major version number mismatch. " + "Expected [%d]; got [%d]\n", + ECRYPTFS_VERSION_MAJOR, major); + rc = -EINVAL; + goto out; + } + if (minor != ECRYPTFS_VERSION_MINOR) { + ecryptfs_printk(KERN_ERR, "Minor version number mismatch. " + "Expected [%d]; got [%d]\n", + ECRYPTFS_VERSION_MINOR, minor); + rc = -EINVAL; + goto out; + } +out: + return rc; +} + +/** + * ecryptfs_parse_options + * @sb: The ecryptfs super block + * @options: The options pased to the kernel + * + * Parse mount options: + * debug=N - ecryptfs_verbosity level for debug output + * sig=XXX - description(signature) of the key to use + * + * Returns the dentry object of the lower-level (lower/interposed) + * directory; We want to mount our stackable file system on top of + * that lower directory. + * + * The signature of the key to use must be the description of a key + * already in the keyring. Mounting will fail if the key can not be + * found. + * + * Returns zero on success; non-zero on error + */ +static int ecryptfs_parse_options(struct super_block *sb, char *options) +{ + char *p; + int rc = 0; + int sig_set = 0; + int cipher_name_set = 0; + int cipher_key_bytes; + int cipher_key_bytes_set = 0; + struct key *auth_tok_key = NULL; + struct ecryptfs_auth_tok *auth_tok = NULL; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat = + &ecryptfs_superblock_to_private(sb)->mount_crypt_stat; + substring_t args[MAX_OPT_ARGS]; + int token; + char *sig_src; + char *sig_dst; + char *debug_src; + char *cipher_name_dst; + char *cipher_name_src; + char *cipher_key_bytes_src; + struct crypto_tfm *tmp_tfm; + int cipher_name_len; + + if (!options) { + rc = -EINVAL; + goto out; + } + while ((p = strsep(&options, ",")) != NULL) { + if (!*p) + continue; + token = match_token(p, tokens, args); + switch (token) { + case ecryptfs_opt_sig: + case ecryptfs_opt_ecryptfs_sig: + sig_src = args[0].from; + sig_dst = + mount_crypt_stat->global_auth_tok_sig; + memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX); + sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + ecryptfs_printk(KERN_DEBUG, + "The mount_crypt_stat " + "global_auth_tok_sig set to: " + "[%s]\n", sig_dst); + sig_set = 1; + break; + case ecryptfs_opt_debug: + case ecryptfs_opt_ecryptfs_debug: + debug_src = args[0].from; + ecryptfs_verbosity = + (int)simple_strtol(debug_src, &debug_src, + 0); + ecryptfs_printk(KERN_DEBUG, + "Verbosity set to [%d]" "\n", + ecryptfs_verbosity); + break; + case ecryptfs_opt_cipher: + case ecryptfs_opt_ecryptfs_cipher: + cipher_name_src = args[0].from; + cipher_name_dst = + mount_crypt_stat-> + global_default_cipher_name; + strncpy(cipher_name_dst, cipher_name_src, + ECRYPTFS_MAX_CIPHER_NAME_SIZE); + ecryptfs_printk(KERN_DEBUG, + "The mount_crypt_stat " + "global_default_cipher_name set to: " + "[%s]\n", cipher_name_dst); + cipher_name_set = 1; + break; + case ecryptfs_opt_ecryptfs_key_bytes: + cipher_key_bytes_src = args[0].from; + cipher_key_bytes = + (int)simple_strtol(cipher_key_bytes_src, + &cipher_key_bytes_src, 0); + mount_crypt_stat->global_default_cipher_key_size = + cipher_key_bytes; + ecryptfs_printk(KERN_DEBUG, + "The mount_crypt_stat " + "global_default_cipher_key_size " + "set to: [%d]\n", mount_crypt_stat-> + global_default_cipher_key_size); + cipher_key_bytes_set = 1; + break; + case ecryptfs_opt_passthrough: + mount_crypt_stat->flags |= + ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED; + break; + case ecryptfs_opt_err: + default: + ecryptfs_printk(KERN_WARNING, + "eCryptfs: unrecognized option '%s'\n", + p); + } + } + /* Do not support lack of mount-wide signature in 0.1 + * release */ + if (!sig_set) { + rc = -EINVAL; + ecryptfs_printk(KERN_ERR, "You must supply a valid " + "passphrase auth tok signature as a mount " + "parameter; see the eCryptfs README\n"); + goto out; + } + if (!cipher_name_set) { + cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER); + if (unlikely(cipher_name_len + >= ECRYPTFS_MAX_CIPHER_NAME_SIZE)) { + rc = -EINVAL; + BUG(); + goto out; + } + memcpy(mount_crypt_stat->global_default_cipher_name, + ECRYPTFS_DEFAULT_CIPHER, cipher_name_len); + mount_crypt_stat->global_default_cipher_name[cipher_name_len] + = '\0'; + } + if (!cipher_key_bytes_set) { + mount_crypt_stat->global_default_cipher_key_size = + ECRYPTFS_DEFAULT_KEY_BYTES; + ecryptfs_printk(KERN_DEBUG, "Cipher key size was not " + "specified. Defaulting to [%d]\n", + mount_crypt_stat-> + global_default_cipher_key_size); + } + rc = ecryptfs_process_cipher( + &tmp_tfm, + &mount_crypt_stat->global_key_tfm, + mount_crypt_stat->global_default_cipher_name, + mount_crypt_stat->global_default_cipher_key_size); + if (tmp_tfm) + crypto_free_tfm(tmp_tfm); + if (rc) { + printk(KERN_ERR "Error attempting to initialize cipher [%s] " + "with key size [%Zd] bytes; rc = [%d]\n", + mount_crypt_stat->global_default_cipher_name, + mount_crypt_stat->global_default_cipher_key_size, rc); + rc = -EINVAL; + goto out; + } + mutex_init(&mount_crypt_stat->global_key_tfm_mutex); + ecryptfs_printk(KERN_DEBUG, "Requesting the key with description: " + "[%s]\n", mount_crypt_stat->global_auth_tok_sig); + /* The reference to this key is held until umount is done The + * call to key_put is done in ecryptfs_put_super() */ + auth_tok_key = request_key(&key_type_user, + mount_crypt_stat->global_auth_tok_sig, + NULL); + if (!auth_tok_key || IS_ERR(auth_tok_key)) { + ecryptfs_printk(KERN_ERR, "Could not find key with " + "description: [%s]\n", + mount_crypt_stat->global_auth_tok_sig); + process_request_key_err(PTR_ERR(auth_tok_key)); + rc = -EINVAL; + goto out; + } + auth_tok = ecryptfs_get_key_payload_data(auth_tok_key); + if (ecryptfs_verify_version(auth_tok->version)) { + ecryptfs_printk(KERN_ERR, "Data structure version mismatch. " + "Userspace tools must match eCryptfs kernel " + "module with major version [%d] and minor " + "version [%d]\n", ECRYPTFS_VERSION_MAJOR, + ECRYPTFS_VERSION_MINOR); + rc = -EINVAL; + goto out; + } + if (auth_tok->token_type != ECRYPTFS_PASSWORD) { + ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure " + "returned from key\n"); + rc = -EINVAL; + goto out; + } + mount_crypt_stat->global_auth_tok_key = auth_tok_key; + mount_crypt_stat->global_auth_tok = auth_tok; +out: + return rc; +} + +struct kmem_cache *ecryptfs_sb_info_cache; + +/** + * ecryptfs_fill_super + * @sb: The ecryptfs super block + * @raw_data: The options passed to mount + * @silent: Not used but required by function prototype + * + * Sets up what we can of the sb, rest is done in ecryptfs_read_super + * + * Returns zero on success; non-zero otherwise + */ +static int +ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent) +{ + int rc = 0; + + /* Released in ecryptfs_put_super() */ + ecryptfs_set_superblock_private(sb, + kmem_cache_alloc(ecryptfs_sb_info_cache, + SLAB_KERNEL)); + if (!ecryptfs_superblock_to_private(sb)) { + ecryptfs_printk(KERN_WARNING, "Out of memory\n"); + rc = -ENOMEM; + goto out; + } + memset(ecryptfs_superblock_to_private(sb), 0, + sizeof(struct ecryptfs_sb_info)); + sb->s_op = &ecryptfs_sops; + /* Released through deactivate_super(sb) from get_sb_nodev */ + sb->s_root = d_alloc(NULL, &(const struct qstr) { + .hash = 0,.name = "/",.len = 1}); + if (!sb->s_root) { + ecryptfs_printk(KERN_ERR, "d_alloc failed\n"); + rc = -ENOMEM; + goto out; + } + sb->s_root->d_op = &ecryptfs_dops; + sb->s_root->d_sb = sb; + sb->s_root->d_parent = sb->s_root; + /* Released in d_release when dput(sb->s_root) is called */ + /* through deactivate_super(sb) from get_sb_nodev() */ + ecryptfs_set_dentry_private(sb->s_root, + kmem_cache_alloc(ecryptfs_dentry_info_cache, + SLAB_KERNEL)); + if (!ecryptfs_dentry_to_private(sb->s_root)) { + ecryptfs_printk(KERN_ERR, + "dentry_info_cache alloc failed\n"); + rc = -ENOMEM; + goto out; + } + memset(ecryptfs_dentry_to_private(sb->s_root), 0, + sizeof(struct ecryptfs_dentry_info)); + rc = 0; +out: + /* Should be able to rely on deactivate_super called from + * get_sb_nodev */ + return rc; +} + +/** + * ecryptfs_read_super + * @sb: The ecryptfs super block + * @dev_name: The path to mount over + * + * Read the super block of the lower filesystem, and use + * ecryptfs_interpose to create our initial inode and super block + * struct. + */ +static int ecryptfs_read_super(struct super_block *sb, const char *dev_name) +{ + int rc; + struct nameidata nd; + struct dentry *lower_root; + struct vfsmount *lower_mnt; + + memset(&nd, 0, sizeof(struct nameidata)); + rc = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); + if (rc) { + ecryptfs_printk(KERN_WARNING, "path_lookup() failed\n"); + goto out_free; + } + lower_root = nd.dentry; + if (!lower_root->d_inode) { + ecryptfs_printk(KERN_WARNING, + "No directory to interpose on\n"); + rc = -ENOENT; + goto out_free; + } + lower_mnt = nd.mnt; + ecryptfs_set_superblock_lower(sb, lower_root->d_sb); + sb->s_maxbytes = lower_root->d_sb->s_maxbytes; + ecryptfs_set_dentry_lower(sb->s_root, lower_root); + ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt); + if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0))) + goto out_free; + rc = 0; + goto out; +out_free: + path_release(&nd); +out: + return rc; +} + +/** + * ecryptfs_get_sb + * @fs_type + * @flags + * @dev_name: The path to mount over + * @raw_data: The options passed into the kernel + * + * The whole ecryptfs_get_sb process is broken into 4 functions: + * ecryptfs_parse_options(): handle options passed to ecryptfs, if any + * ecryptfs_fill_super(): used by get_sb_nodev, fills out the super_block + * with as much information as it can before needing + * the lower filesystem. + * ecryptfs_read_super(): this accesses the lower filesystem and uses + * ecryptfs_interpolate to perform most of the linking + * ecryptfs_interpolate(): links the lower filesystem into ecryptfs + */ +static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data, + struct vfsmount *mnt) +{ + int rc; + struct super_block *sb; + + rc = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super, mnt); + if (rc < 0) { + printk(KERN_ERR "Getting sb failed; rc = [%d]\n", rc); + goto out; + } + sb = mnt->mnt_sb; + rc = ecryptfs_parse_options(sb, raw_data); + if (rc) { + printk(KERN_ERR "Error parsing options; rc = [%d]\n", rc); + goto out_abort; + } + rc = ecryptfs_read_super(sb, dev_name); + if (rc) { + printk(KERN_ERR "Reading sb failed; rc = [%d]\n", rc); + goto out_abort; + } + goto out; +out_abort: + dput(sb->s_root); + up_write(&sb->s_umount); + deactivate_super(sb); +out: + return rc; +} + +/** + * ecryptfs_kill_block_super + * @sb: The ecryptfs super block + * + * Used to bring the superblock down and free the private data. + * Private data is free'd in ecryptfs_put_super() + */ +static void ecryptfs_kill_block_super(struct super_block *sb) +{ + generic_shutdown_super(sb); +} + +static struct file_system_type ecryptfs_fs_type = { + .owner = THIS_MODULE, + .name = "ecryptfs", + .get_sb = ecryptfs_get_sb, + .kill_sb = ecryptfs_kill_block_super, + .fs_flags = 0 +}; + +/** + * inode_info_init_once + * + * Initializes the ecryptfs_inode_info_cache when it is created + */ +static void +inode_info_init_once(void *vptr, struct kmem_cache *cachep, unsigned long flags) +{ + struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static struct ecryptfs_cache_info { + kmem_cache_t **cache; + const char *name; + size_t size; + void (*ctor)(void*, struct kmem_cache *, unsigned long); +} ecryptfs_cache_infos[] = { + { + .cache = &ecryptfs_auth_tok_list_item_cache, + .name = "ecryptfs_auth_tok_list_item", + .size = sizeof(struct ecryptfs_auth_tok_list_item), + }, + { + .cache = &ecryptfs_file_info_cache, + .name = "ecryptfs_file_cache", + .size = sizeof(struct ecryptfs_file_info), + }, + { + .cache = &ecryptfs_dentry_info_cache, + .name = "ecryptfs_dentry_info_cache", + .size = sizeof(struct ecryptfs_dentry_info), + }, + { + .cache = &ecryptfs_inode_info_cache, + .name = "ecryptfs_inode_cache", + .size = sizeof(struct ecryptfs_inode_info), + .ctor = inode_info_init_once, + }, + { + .cache = &ecryptfs_sb_info_cache, + .name = "ecryptfs_sb_cache", + .size = sizeof(struct ecryptfs_sb_info), + }, + { + .cache = &ecryptfs_header_cache_0, + .name = "ecryptfs_headers_0", + .size = PAGE_CACHE_SIZE, + }, + { + .cache = &ecryptfs_header_cache_1, + .name = "ecryptfs_headers_1", + .size = PAGE_CACHE_SIZE, + }, + { + .cache = &ecryptfs_header_cache_2, + .name = "ecryptfs_headers_2", + .size = PAGE_CACHE_SIZE, + }, + { + .cache = &ecryptfs_lower_page_cache, + .name = "ecryptfs_lower_page_cache", + .size = PAGE_CACHE_SIZE, + }, +}; + +static void ecryptfs_free_kmem_caches(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) { + struct ecryptfs_cache_info *info; + + info = &ecryptfs_cache_infos[i]; + if (*(info->cache)) + kmem_cache_destroy(*(info->cache)); + } +} + +/** + * ecryptfs_init_kmem_caches + * + * Returns zero on success; non-zero otherwise + */ +static int ecryptfs_init_kmem_caches(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) { + struct ecryptfs_cache_info *info; + + info = &ecryptfs_cache_infos[i]; + *(info->cache) = kmem_cache_create(info->name, info->size, + 0, SLAB_HWCACHE_ALIGN, info->ctor, NULL); + if (!*(info->cache)) { + ecryptfs_free_kmem_caches(); + ecryptfs_printk(KERN_WARNING, "%s: " + "kmem_cache_create failed\n", + info->name); + return -ENOMEM; + } + } + return 0; +} + +struct ecryptfs_obj { + char *name; + struct list_head slot_list; + struct kobject kobj; +}; + +struct ecryptfs_attribute { + struct attribute attr; + ssize_t(*show) (struct ecryptfs_obj *, char *); + ssize_t(*store) (struct ecryptfs_obj *, const char *, size_t); +}; + +static ssize_t +ecryptfs_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj, + kobj); + struct ecryptfs_attribute *attribute = + container_of(attr, struct ecryptfs_attribute, attr); + + return (attribute->store ? attribute->store(obj, buf, len) : 0); +} + +static ssize_t +ecryptfs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj, + kobj); + struct ecryptfs_attribute *attribute = + container_of(attr, struct ecryptfs_attribute, attr); + + return (attribute->show ? attribute->show(obj, buf) : 0); +} + +static struct sysfs_ops ecryptfs_sysfs_ops = { + .show = ecryptfs_attr_show, + .store = ecryptfs_attr_store +}; + +static struct kobj_type ecryptfs_ktype = { + .sysfs_ops = &ecryptfs_sysfs_ops +}; + +static decl_subsys(ecryptfs, &ecryptfs_ktype, NULL); + +static ssize_t version_show(struct ecryptfs_obj *obj, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "%d\n", ECRYPTFS_VERSIONING_MASK); +} + +static struct ecryptfs_attribute sysfs_attr_version = __ATTR_RO(version); + +struct ecryptfs_version_str_map_elem { + u32 flag; + char *str; +} ecryptfs_version_str_map[] = { + {ECRYPTFS_VERSIONING_PASSPHRASE, "passphrase"}, + {ECRYPTFS_VERSIONING_PUBKEY, "pubkey"}, + {ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH, "plaintext passthrough"}, + {ECRYPTFS_VERSIONING_POLICY, "policy"} +}; + +static ssize_t version_str_show(struct ecryptfs_obj *obj, char *buff) +{ + int i; + int remaining = PAGE_SIZE; + int total_written = 0; + + buff[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(ecryptfs_version_str_map); i++) { + int entry_size; + + if (!(ECRYPTFS_VERSIONING_MASK + & ecryptfs_version_str_map[i].flag)) + continue; + entry_size = strlen(ecryptfs_version_str_map[i].str); + if ((entry_size + 2) > remaining) + goto out; + memcpy(buff, ecryptfs_version_str_map[i].str, entry_size); + buff[entry_size++] = '\n'; + buff[entry_size] = '\0'; + buff += entry_size; + total_written += entry_size; + remaining -= entry_size; + } +out: + return total_written; +} + +static struct ecryptfs_attribute sysfs_attr_version_str = __ATTR_RO(version_str); + +static int do_sysfs_registration(void) +{ + int rc; + + if ((rc = subsystem_register(&ecryptfs_subsys))) { + printk(KERN_ERR + "Unable to register ecryptfs sysfs subsystem\n"); + goto out; + } + rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj, + &sysfs_attr_version.attr); + if (rc) { + printk(KERN_ERR + "Unable to create ecryptfs version attribute\n"); + subsystem_unregister(&ecryptfs_subsys); + goto out; + } + rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj, + &sysfs_attr_version_str.attr); + if (rc) { + printk(KERN_ERR + "Unable to create ecryptfs version_str attribute\n"); + sysfs_remove_file(&ecryptfs_subsys.kset.kobj, + &sysfs_attr_version.attr); + subsystem_unregister(&ecryptfs_subsys); + goto out; + } +out: + return rc; +} + +static int __init ecryptfs_init(void) +{ + int rc; + + if (ECRYPTFS_DEFAULT_EXTENT_SIZE > PAGE_CACHE_SIZE) { + rc = -EINVAL; + ecryptfs_printk(KERN_ERR, "The eCryptfs extent size is " + "larger than the host's page size, and so " + "eCryptfs cannot run on this system. The " + "default eCryptfs extent size is [%d] bytes; " + "the page size is [%d] bytes.\n", + ECRYPTFS_DEFAULT_EXTENT_SIZE, PAGE_CACHE_SIZE); + goto out; + } + rc = ecryptfs_init_kmem_caches(); + if (rc) { + printk(KERN_ERR + "Failed to allocate one or more kmem_cache objects\n"); + goto out; + } + rc = register_filesystem(&ecryptfs_fs_type); + if (rc) { + printk(KERN_ERR "Failed to register filesystem\n"); + ecryptfs_free_kmem_caches(); + goto out; + } + kset_set_kset_s(&ecryptfs_subsys, fs_subsys); + sysfs_attr_version.attr.owner = THIS_MODULE; + sysfs_attr_version_str.attr.owner = THIS_MODULE; + rc = do_sysfs_registration(); + if (rc) { + printk(KERN_ERR "sysfs registration failed\n"); + unregister_filesystem(&ecryptfs_fs_type); + ecryptfs_free_kmem_caches(); + goto out; + } +out: + return rc; +} + +static void __exit ecryptfs_exit(void) +{ + sysfs_remove_file(&ecryptfs_subsys.kset.kobj, + &sysfs_attr_version.attr); + sysfs_remove_file(&ecryptfs_subsys.kset.kobj, + &sysfs_attr_version_str.attr); + subsystem_unregister(&ecryptfs_subsys); + unregister_filesystem(&ecryptfs_fs_type); + ecryptfs_free_kmem_caches(); +} + +MODULE_AUTHOR("Michael A. Halcrow "); +MODULE_DESCRIPTION("eCryptfs"); + +MODULE_LICENSE("GPL"); + +module_init(ecryptfs_init) +module_exit(ecryptfs_exit) diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c new file mode 100644 index 0000000000..924dd90a4c --- /dev/null +++ b/fs/ecryptfs/mmap.c @@ -0,0 +1,788 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * This is where eCryptfs coordinates the symmetric encryption and + * decryption of the file data as it passes between the lower + * encrypted file and the upper decrypted file. + * + * Copyright (C) 1997-2003 Erez Zadok + * Copyright (C) 2001-2003 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +struct kmem_cache *ecryptfs_lower_page_cache; + +/** + * ecryptfs_get1page + * + * Get one page from cache or lower f/s, return error otherwise. + * + * Returns unlocked and up-to-date page (if ok), with increased + * refcnt. + */ +static struct page *ecryptfs_get1page(struct file *file, int index) +{ + struct page *page; + struct dentry *dentry; + struct inode *inode; + struct address_space *mapping; + + dentry = file->f_dentry; + inode = dentry->d_inode; + mapping = inode->i_mapping; + page = read_cache_page(mapping, index, + (filler_t *)mapping->a_ops->readpage, + (void *)file); + if (IS_ERR(page)) + goto out; + wait_on_page_locked(page); +out: + return page; +} + +static +int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros); + +/** + * ecryptfs_fill_zeros + * @file: The ecryptfs file + * @new_length: The new length of the data in the underlying file; + * everything between the prior end of the file and the + * new end of the file will be filled with zero's. + * new_length must be greater than current length + * + * Function for handling lseek-ing past the end of the file. + * + * This function does not support shrinking, only growing a file. + * + * Returns zero on success; non-zero otherwise. + */ +int ecryptfs_fill_zeros(struct file *file, loff_t new_length) +{ + int rc = 0; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + pgoff_t old_end_page_index = 0; + pgoff_t index = old_end_page_index; + int old_end_pos_in_page = -1; + pgoff_t new_end_page_index; + int new_end_pos_in_page; + loff_t cur_length = i_size_read(inode); + + if (cur_length != 0) { + index = old_end_page_index = + ((cur_length - 1) >> PAGE_CACHE_SHIFT); + old_end_pos_in_page = ((cur_length - 1) & ~PAGE_CACHE_MASK); + } + new_end_page_index = ((new_length - 1) >> PAGE_CACHE_SHIFT); + new_end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK); + ecryptfs_printk(KERN_DEBUG, "old_end_page_index = [0x%.16x]; " + "old_end_pos_in_page = [%d]; " + "new_end_page_index = [0x%.16x]; " + "new_end_pos_in_page = [%d]\n", + old_end_page_index, old_end_pos_in_page, + new_end_page_index, new_end_pos_in_page); + if (old_end_page_index == new_end_page_index) { + /* Start and end are in the same page; we just need to + * set a portion of the existing page to zero's */ + rc = write_zeros(file, index, (old_end_pos_in_page + 1), + (new_end_pos_in_page - old_end_pos_in_page)); + if (rc) + ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " + "index=[0x%.16x], " + "old_end_pos_in_page=[d], " + "(PAGE_CACHE_SIZE - new_end_pos_in_page" + "=[%d]" + ")=[d]) returned [%d]\n", file, index, + old_end_pos_in_page, + new_end_pos_in_page, + (PAGE_CACHE_SIZE - new_end_pos_in_page), + rc); + goto out; + } + /* Fill the remainder of the previous last page with zeros */ + rc = write_zeros(file, index, (old_end_pos_in_page + 1), + ((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page)); + if (rc) { + ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " + "index=[0x%.16x], old_end_pos_in_page=[d], " + "(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) " + "returned [%d]\n", file, index, + old_end_pos_in_page, + (PAGE_CACHE_SIZE - old_end_pos_in_page), rc); + goto out; + } + index++; + while (index < new_end_page_index) { + /* Fill all intermediate pages with zeros */ + rc = write_zeros(file, index, 0, PAGE_CACHE_SIZE); + if (rc) { + ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " + "index=[0x%.16x], " + "old_end_pos_in_page=[d], " + "(PAGE_CACHE_SIZE - new_end_pos_in_page" + "=[%d]" + ")=[d]) returned [%d]\n", file, index, + old_end_pos_in_page, + new_end_pos_in_page, + (PAGE_CACHE_SIZE - new_end_pos_in_page), + rc); + goto out; + } + index++; + } + /* Fill the portion at the beginning of the last new page with + * zero's */ + rc = write_zeros(file, index, 0, (new_end_pos_in_page + 1)); + if (rc) { + ecryptfs_printk(KERN_ERR, "write_zeros(file=" + "[%p], index=[0x%.16x], 0, " + "new_end_pos_in_page=[%d]" + "returned [%d]\n", file, index, + new_end_pos_in_page, rc); + goto out; + } +out: + return rc; +} + +/** + * ecryptfs_writepage + * @page: Page that is locked before this call is made + * + * Returns zero on success; non-zero otherwise + */ +static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct ecryptfs_page_crypt_context ctx; + int rc; + + ctx.page = page; + ctx.mode = ECRYPTFS_WRITEPAGE_MODE; + ctx.param.wbc = wbc; + rc = ecryptfs_encrypt_page(&ctx); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error encrypting " + "page (upper index [0x%.16x])\n", page->index); + ClearPageUptodate(page); + goto out; + } + SetPageUptodate(page); + unlock_page(page); +out: + return rc; +} + +/** + * Reads the data from the lower file file at index lower_page_index + * and copies that data into page. + * + * @param page Page to fill + * @param lower_page_index Index of the page in the lower file to get + */ +int ecryptfs_do_readpage(struct file *file, struct page *page, + pgoff_t lower_page_index) +{ + int rc; + struct dentry *dentry; + struct file *lower_file; + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + char *page_data; + struct page *lower_page = NULL; + char *lower_page_data; + const struct address_space_operations *lower_a_ops; + + dentry = file->f_dentry; + lower_file = ecryptfs_file_to_lower(file); + lower_dentry = ecryptfs_dentry_to_lower(dentry); + inode = dentry->d_inode; + lower_inode = ecryptfs_inode_to_lower(inode); + lower_a_ops = lower_inode->i_mapping->a_ops; + lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index, + (filler_t *)lower_a_ops->readpage, + (void *)lower_file); + if (IS_ERR(lower_page)) { + rc = PTR_ERR(lower_page); + lower_page = NULL; + ecryptfs_printk(KERN_ERR, "Error reading from page cache\n"); + goto out; + } + wait_on_page_locked(lower_page); + page_data = (char *)kmap(page); + if (!page_data) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error mapping page\n"); + goto out; + } + lower_page_data = (char *)kmap(lower_page); + if (!lower_page_data) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error mapping page\n"); + kunmap(page); + goto out; + } + memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE); + kunmap(lower_page); + kunmap(page); + rc = 0; +out: + if (likely(lower_page)) + page_cache_release(lower_page); + if (rc == 0) + SetPageUptodate(page); + else + ClearPageUptodate(page); + return rc; +} + +/** + * ecryptfs_readpage + * @file: This is an ecryptfs file + * @page: ecryptfs associated page to stick the read data into + * + * Read in a page, decrypting if necessary. + * + * Returns zero on success; non-zero on error. + */ +static int ecryptfs_readpage(struct file *file, struct page *page) +{ + int rc = 0; + struct ecryptfs_crypt_stat *crypt_stat; + + BUG_ON(!(file && file->f_dentry && file->f_dentry->d_inode)); + crypt_stat = + &ecryptfs_inode_to_private(file->f_dentry->d_inode)->crypt_stat; + if (!crypt_stat + || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED) + || ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) { + ecryptfs_printk(KERN_DEBUG, + "Passing through unencrypted page\n"); + rc = ecryptfs_do_readpage(file, page, page->index); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error reading page; rc = " + "[%d]\n", rc); + goto out; + } + } else { + rc = ecryptfs_decrypt_page(file, page); + if (rc) { + + ecryptfs_printk(KERN_ERR, "Error decrypting page; " + "rc = [%d]\n", rc); + goto out; + } + } + SetPageUptodate(page); +out: + if (rc) + ClearPageUptodate(page); + ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n", + page->index); + unlock_page(page); + return rc; +} + +static int fill_zeros_to_end_of_page(struct page *page, unsigned int to) +{ + struct inode *inode = page->mapping->host; + int end_byte_in_page; + int rc = 0; + char *page_virt; + + if ((i_size_read(inode) / PAGE_CACHE_SIZE) == page->index) { + end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE; + if (to > end_byte_in_page) + end_byte_in_page = to; + page_virt = kmap(page); + if (!page_virt) { + rc = -ENOMEM; + ecryptfs_printk(KERN_WARNING, + "Could not map page\n"); + goto out; + } + memset((page_virt + end_byte_in_page), 0, + (PAGE_CACHE_SIZE - end_byte_in_page)); + kunmap(page); + } +out: + return rc; +} + +static int ecryptfs_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + int rc = 0; + + kmap(page); + if (from == 0 && to == PAGE_CACHE_SIZE) + goto out; /* If we are writing a full page, it will be + up to date. */ + if (!PageUptodate(page)) + rc = ecryptfs_do_readpage(file, page, page->index); +out: + return rc; +} + +int ecryptfs_grab_and_map_lower_page(struct page **lower_page, + char **lower_virt, + struct inode *lower_inode, + unsigned long lower_page_index) +{ + int rc = 0; + + (*lower_page) = grab_cache_page(lower_inode->i_mapping, + lower_page_index); + if (!(*lower_page)) { + ecryptfs_printk(KERN_ERR, "grab_cache_page for " + "lower_page_index = [0x%.16x] failed\n", + lower_page_index); + rc = -EINVAL; + goto out; + } + if (lower_virt) + (*lower_virt) = kmap((*lower_page)); + else + kmap((*lower_page)); +out: + return rc; +} + +int ecryptfs_writepage_and_release_lower_page(struct page *lower_page, + struct inode *lower_inode, + struct writeback_control *wbc) +{ + int rc = 0; + + rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error calling lower writepage(); " + "rc = [%d]\n", rc); + goto out; + } + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + page_cache_release(lower_page); +out: + return rc; +} + +static void ecryptfs_unmap_and_release_lower_page(struct page *lower_page) +{ + kunmap(lower_page); + ecryptfs_printk(KERN_DEBUG, "Unlocking lower page with index = " + "[0x%.16x]\n", lower_page->index); + unlock_page(lower_page); + page_cache_release(lower_page); +} + +/** + * ecryptfs_write_inode_size_to_header + * + * Writes the lower file size to the first 8 bytes of the header. + * + * Returns zero on success; non-zero on error. + */ +int +ecryptfs_write_inode_size_to_header(struct file *lower_file, + struct inode *lower_inode, + struct inode *inode) +{ + int rc = 0; + struct page *header_page; + char *header_virt; + const struct address_space_operations *lower_a_ops; + u64 file_size; + + rc = ecryptfs_grab_and_map_lower_page(&header_page, &header_virt, + lower_inode, 0); + if (rc) { + ecryptfs_printk(KERN_ERR, "grab_cache_page for header page " + "failed\n"); + goto out; + } + lower_a_ops = lower_inode->i_mapping->a_ops; + rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8); + file_size = (u64)i_size_read(inode); + ecryptfs_printk(KERN_DEBUG, "Writing size: [0x%.16x]\n", file_size); + file_size = cpu_to_be64(file_size); + memcpy(header_virt, &file_size, sizeof(u64)); + rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8); + if (rc < 0) + ecryptfs_printk(KERN_ERR, "Error commiting header page " + "write\n"); + ecryptfs_unmap_and_release_lower_page(header_page); + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty_sync(inode); +out: + return rc; +} + +int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode, + struct file *lower_file, + unsigned long lower_page_index, int byte_offset, + int region_bytes) +{ + int rc = 0; + + rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, lower_inode, + lower_page_index); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to grab and map " + "lower page with index [0x%.16x]\n", + lower_page_index); + goto out; + } + rc = lower_inode->i_mapping->a_ops->prepare_write(lower_file, + (*lower_page), + byte_offset, + region_bytes); + if (rc) { + ecryptfs_printk(KERN_ERR, "prepare_write for " + "lower_page_index = [0x%.16x] failed; rc = " + "[%d]\n", lower_page_index, rc); + } +out: + if (rc && (*lower_page)) { + ecryptfs_unmap_and_release_lower_page(*lower_page); + (*lower_page) = NULL; + } + return rc; +} + +/** + * ecryptfs_commit_lower_page + * + * Returns zero on success; non-zero on error + */ +int +ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode, + struct file *lower_file, int byte_offset, + int region_size) +{ + int rc = 0; + + rc = lower_inode->i_mapping->a_ops->commit_write( + lower_file, lower_page, byte_offset, region_size); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, + "Error committing write; rc = [%d]\n", rc); + } else + rc = 0; + ecryptfs_unmap_and_release_lower_page(lower_page); + return rc; +} + +/** + * ecryptfs_copy_page_to_lower + * + * Used for plaintext pass-through; no page index interpolation + * required. + */ +int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode, + struct file *lower_file) +{ + int rc = 0; + struct page *lower_page; + + rc = ecryptfs_get_lower_page(&lower_page, lower_inode, lower_file, + page->index, 0, PAGE_CACHE_SIZE); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error attempting to get page " + "at index [0x%.16x]\n", page->index); + goto out; + } + /* TODO: aops */ + memcpy((char *)page_address(lower_page), page_address(page), + PAGE_CACHE_SIZE); + rc = ecryptfs_commit_lower_page(lower_page, lower_inode, lower_file, + 0, PAGE_CACHE_SIZE); + if (rc) + ecryptfs_printk(KERN_ERR, "Error attempting to commit page " + "at index [0x%.16x]\n", page->index); +out: + return rc; +} + +static int +process_new_file(struct ecryptfs_crypt_stat *crypt_stat, + struct file *file, struct inode *inode) +{ + struct page *header_page; + const struct address_space_operations *lower_a_ops; + struct inode *lower_inode; + struct file *lower_file; + char *header_virt; + int rc = 0; + int current_header_page = 0; + int header_pages; + int more_header_data_to_be_written = 1; + + lower_inode = ecryptfs_inode_to_lower(inode); + lower_file = ecryptfs_file_to_lower(file); + lower_a_ops = lower_inode->i_mapping->a_ops; + header_pages = ((crypt_stat->header_extent_size + * crypt_stat->num_header_extents_at_front) + / PAGE_CACHE_SIZE); + BUG_ON(header_pages < 1); + while (current_header_page < header_pages) { + rc = ecryptfs_grab_and_map_lower_page(&header_page, + &header_virt, + lower_inode, + current_header_page); + if (rc) { + ecryptfs_printk(KERN_ERR, "grab_cache_page for " + "header page [%d] failed; rc = [%d]\n", + current_header_page, rc); + goto out; + } + rc = lower_a_ops->prepare_write(lower_file, header_page, 0, + PAGE_CACHE_SIZE); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error preparing to write " + "header page out; rc = [%d]\n", rc); + goto out; + } + memset(header_virt, 0, PAGE_CACHE_SIZE); + if (more_header_data_to_be_written) { + rc = ecryptfs_write_headers_virt(header_virt, + crypt_stat, + file->f_dentry); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error " + "generating header; rc = " + "[%d]\n", rc); + rc = -EIO; + memset(header_virt, 0, PAGE_CACHE_SIZE); + ecryptfs_unmap_and_release_lower_page( + header_page); + goto out; + } + if (current_header_page == 0) + memset(header_virt, 0, 8); + more_header_data_to_be_written = 0; + } + rc = lower_a_ops->commit_write(lower_file, header_page, 0, + PAGE_CACHE_SIZE); + ecryptfs_unmap_and_release_lower_page(header_page); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, + "Error commiting header page write; " + "rc = [%d]\n", rc); + break; + } + current_header_page++; + } + if (rc >= 0) { + rc = 0; + ecryptfs_printk(KERN_DEBUG, "lower_inode->i_blocks = " + "[0x%.16x]\n", lower_inode->i_blocks); + i_size_write(inode, 0); + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty_sync(inode); + } + ecryptfs_printk(KERN_DEBUG, "Clearing ECRYPTFS_NEW_FILE flag in " + "crypt_stat at memory location [%p]\n", crypt_stat); + ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE); +out: + return rc; +} + +/** + * ecryptfs_commit_write + * @file: The eCryptfs file object + * @page: The eCryptfs page + * @from: Ignored (we rotate the page IV on each write) + * @to: Ignored + * + * This is where we encrypt the data and pass the encrypted data to + * the lower filesystem. In OpenPGP-compatible mode, we operate on + * entire underlying packets. + */ +static int ecryptfs_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + struct ecryptfs_page_crypt_context ctx; + loff_t pos; + struct inode *inode; + struct inode *lower_inode; + struct file *lower_file; + struct ecryptfs_crypt_stat *crypt_stat; + int rc; + + inode = page->mapping->host; + lower_inode = ecryptfs_inode_to_lower(inode); + lower_file = ecryptfs_file_to_lower(file); + mutex_lock(&lower_inode->i_mutex); + crypt_stat = + &ecryptfs_inode_to_private(file->f_dentry->d_inode)->crypt_stat; + if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) { + ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_NEW_FILE flag set in " + "crypt_stat at memory location [%p]\n", crypt_stat); + rc = process_new_file(crypt_stat, file, inode); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error processing new " + "file; rc = [%d]\n", rc); + goto out; + } + } else + ecryptfs_printk(KERN_DEBUG, "Not a new file\n"); + ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page" + "(page w/ index = [0x%.16x], to = [%d])\n", page->index, + to); + rc = fill_zeros_to_end_of_page(page, to); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error attempting to fill " + "zeros in page with index = [0x%.16x]\n", + page->index); + goto out; + } + ctx.page = page; + ctx.mode = ECRYPTFS_PREPARE_COMMIT_MODE; + ctx.param.lower_file = lower_file; + rc = ecryptfs_encrypt_page(&ctx); + if (rc) { + ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper " + "index [0x%.16x])\n", page->index); + goto out; + } + rc = 0; + inode->i_blocks = lower_inode->i_blocks; + pos = (page->index << PAGE_CACHE_SHIFT) + to; + if (pos > i_size_read(inode)) { + i_size_write(inode, pos); + ecryptfs_printk(KERN_DEBUG, "Expanded file size to " + "[0x%.16x]\n", i_size_read(inode)); + } + ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode); + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty_sync(inode); +out: + kunmap(page); /* mapped in prior call (prepare_write) */ + if (rc < 0) + ClearPageUptodate(page); + else + SetPageUptodate(page); + mutex_unlock(&lower_inode->i_mutex); + return rc; +} + +/** + * write_zeros + * @file: The ecryptfs file + * @index: The index in which we are writing + * @start: The position after the last block of data + * @num_zeros: The number of zeros to write + * + * Write a specified number of zero's to a page. + * + * (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE + */ +static +int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros) +{ + int rc = 0; + struct page *tmp_page; + + tmp_page = ecryptfs_get1page(file, index); + if (IS_ERR(tmp_page)) { + ecryptfs_printk(KERN_ERR, "Error getting page at index " + "[0x%.16x]\n", index); + rc = PTR_ERR(tmp_page); + goto out; + } + kmap(tmp_page); + rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error preparing to write zero's " + "to remainder of page at index [0x%.16x]\n", + index); + kunmap(tmp_page); + page_cache_release(tmp_page); + goto out; + } + memset(((char *)page_address(tmp_page) + start), 0, num_zeros); + rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, "Error attempting to write zero's " + "to remainder of page at index [0x%.16x]\n", + index); + kunmap(tmp_page); + page_cache_release(tmp_page); + goto out; + } + rc = 0; + kunmap(tmp_page); + page_cache_release(tmp_page); +out: + return rc; +} + +static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block) +{ + int rc = 0; + struct inode *inode; + struct inode *lower_inode; + + inode = (struct inode *)mapping->host; + lower_inode = ecryptfs_inode_to_lower(inode); + if (lower_inode->i_mapping->a_ops->bmap) + rc = lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping, + block); + return rc; +} + +static void ecryptfs_sync_page(struct page *page) +{ + struct inode *inode; + struct inode *lower_inode; + struct page *lower_page; + + inode = page->mapping->host; + lower_inode = ecryptfs_inode_to_lower(inode); + /* NOTE: Recently swapped with grab_cache_page(), since + * sync_page() just makes sure that pending I/O gets done. */ + lower_page = find_lock_page(lower_inode->i_mapping, page->index); + if (!lower_page) { + ecryptfs_printk(KERN_DEBUG, "find_lock_page failed\n"); + return; + } + lower_page->mapping->a_ops->sync_page(lower_page); + ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n", + lower_page->index); + unlock_page(lower_page); + page_cache_release(lower_page); +} + +struct address_space_operations ecryptfs_aops = { + .writepage = ecryptfs_writepage, + .readpage = ecryptfs_readpage, + .prepare_write = ecryptfs_prepare_write, + .commit_write = ecryptfs_commit_write, + .bmap = ecryptfs_bmap, + .sync_page = ecryptfs_sync_page, +}; diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c new file mode 100644 index 0000000000..c337c0410f --- /dev/null +++ b/fs/ecryptfs/super.c @@ -0,0 +1,198 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (C) 1997-2003 Erez Zadok + * Copyright (C) 2001-2003 Stony Brook University + * Copyright (C) 2004-2006 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +struct kmem_cache *ecryptfs_inode_info_cache; + +/** + * ecryptfs_alloc_inode - allocate an ecryptfs inode + * @sb: Pointer to the ecryptfs super block + * + * Called to bring an inode into existence. + * + * Only handle allocation, setting up structures should be done in + * ecryptfs_read_inode. This is because the kernel, between now and + * then, will 0 out the private data pointer. + * + * Returns a pointer to a newly allocated inode, NULL otherwise + */ +static struct inode *ecryptfs_alloc_inode(struct super_block *sb) +{ + struct ecryptfs_inode_info *ecryptfs_inode; + struct inode *inode = NULL; + + ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache, + SLAB_KERNEL); + if (unlikely(!ecryptfs_inode)) + goto out; + ecryptfs_init_crypt_stat(&ecryptfs_inode->crypt_stat); + inode = &ecryptfs_inode->vfs_inode; +out: + return inode; +} + +/** + * ecryptfs_destroy_inode + * @inode: The ecryptfs inode + * + * This is used during the final destruction of the inode. + * All allocation of memory related to the inode, including allocated + * memory in the crypt_stat struct, will be released here. + * There should be no chance that this deallocation will be missed. + */ +static void ecryptfs_destroy_inode(struct inode *inode) +{ + struct ecryptfs_inode_info *inode_info; + + inode_info = ecryptfs_inode_to_private(inode); + ecryptfs_destruct_crypt_stat(&inode_info->crypt_stat); + kmem_cache_free(ecryptfs_inode_info_cache, inode_info); +} + +/** + * ecryptfs_init_inode + * @inode: The ecryptfs inode + * + * Set up the ecryptfs inode. + */ +void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode) +{ + ecryptfs_set_inode_lower(inode, lower_inode); + inode->i_ino = lower_inode->i_ino; + inode->i_version++; + inode->i_op = &ecryptfs_main_iops; + inode->i_fop = &ecryptfs_main_fops; + inode->i_mapping->a_ops = &ecryptfs_aops; +} + +/** + * ecryptfs_put_super + * @sb: Pointer to the ecryptfs super block + * + * Final actions when unmounting a file system. + * This will handle deallocation and release of our private data. + */ +static void ecryptfs_put_super(struct super_block *sb) +{ + struct ecryptfs_sb_info *sb_info = ecryptfs_superblock_to_private(sb); + + ecryptfs_destruct_mount_crypt_stat(&sb_info->mount_crypt_stat); + kmem_cache_free(ecryptfs_sb_info_cache, sb_info); + ecryptfs_set_superblock_private(sb, NULL); +} + +/** + * ecryptfs_statfs + * @sb: The ecryptfs super block + * @buf: The struct kstatfs to fill in with stats + * + * Get the filesystem statistics. Currently, we let this pass right through + * to the lower filesystem and take no action ourselves. + */ +static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + return vfs_statfs(ecryptfs_dentry_to_lower(dentry), buf); +} + +/** + * ecryptfs_clear_inode + * @inode - The ecryptfs inode + * + * Called by iput() when the inode reference count reached zero + * and the inode is not hashed anywhere. Used to clear anything + * that needs to be, before the inode is completely destroyed and put + * on the inode free list. We use this to drop out reference to the + * lower inode. + */ +static void ecryptfs_clear_inode(struct inode *inode) +{ + iput(ecryptfs_inode_to_lower(inode)); +} + +/** + * ecryptfs_umount_begin + * + * Called in do_umount(). + */ +static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags) +{ + struct vfsmount *lower_mnt = + ecryptfs_dentry_to_lower_mnt(vfsmnt->mnt_sb->s_root); + struct super_block *lower_sb; + + mntput(lower_mnt); + lower_sb = lower_mnt->mnt_sb; + if (lower_sb->s_op->umount_begin) + lower_sb->s_op->umount_begin(lower_mnt, flags); +} + +/** + * ecryptfs_show_options + * + * Prints the directory we are currently mounted over. + * Returns zero on success; non-zero otherwise + */ +static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct super_block *sb = mnt->mnt_sb; + struct dentry *lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root); + struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(sb->s_root); + char *tmp_page; + char *path; + int rc = 0; + + tmp_page = (char *)__get_free_page(GFP_KERNEL); + if (!tmp_page) { + rc = -ENOMEM; + goto out; + } + path = d_path(lower_root_dentry, lower_mnt, tmp_page, PAGE_SIZE); + if (IS_ERR(path)) { + rc = PTR_ERR(path); + goto out; + } + seq_printf(m, ",dir=%s", path); + free_page((unsigned long)tmp_page); +out: + return rc; +} + +struct super_operations ecryptfs_sops = { + .alloc_inode = ecryptfs_alloc_inode, + .destroy_inode = ecryptfs_destroy_inode, + .drop_inode = generic_delete_inode, + .put_super = ecryptfs_put_super, + .statfs = ecryptfs_statfs, + .remount_fs = NULL, + .clear_inode = ecryptfs_clear_inode, + .umount_begin = ecryptfs_umount_begin, + .show_options = ecryptfs_show_options +}; diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 87e1d03e82..e8c7765419 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -143,43 +143,13 @@ u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) * server crash. */ -/* - * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number, - * that we mark locks for reclaiming, and that we bump the pseudo NSM state. - */ -static void nlmclnt_prepare_reclaim(struct nlm_host *host) -{ - down_write(&host->h_rwsem); - host->h_monitored = 0; - host->h_state++; - host->h_nextrebind = 0; - nlm_rebind_host(host); - - /* - * Mark the locks for reclaiming. - */ - list_splice_init(&host->h_granted, &host->h_reclaim); - - dprintk("NLM: reclaiming locks for host %s\n", host->h_name); -} - -static void nlmclnt_finish_reclaim(struct nlm_host *host) -{ - host->h_reclaiming = 0; - up_write(&host->h_rwsem); - dprintk("NLM: done reclaiming locks for host %s", host->h_name); -} - /* * Reclaim all locks on server host. We do this by spawning a separate * reclaimer thread. */ void -nlmclnt_recovery(struct nlm_host *host, u32 newstate) +nlmclnt_recovery(struct nlm_host *host) { - if (host->h_nsmstate == newstate) - return; - host->h_nsmstate = newstate; if (!host->h_reclaiming++) { nlm_get_host(host); __module_get(THIS_MODULE); @@ -199,18 +169,30 @@ reclaimer(void *ptr) daemonize("%s-reclaim", host->h_name); allow_signal(SIGKILL); + down_write(&host->h_rwsem); + /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ lock_kernel(); lockd_up(0); /* note: this cannot fail as lockd is already running */ - nlmclnt_prepare_reclaim(host); - /* First, reclaim all locks that have been marked. */ + dprintk("lockd: reclaiming locks for host %s", host->h_name); + restart: nsmstate = host->h_nsmstate; + + /* Force a portmap getport - the peer's lockd will + * most likely end up on a different port. + */ + host->h_nextrebind = jiffies; + nlm_rebind_host(host); + + /* First, reclaim all locks that have been granted. */ + list_splice_init(&host->h_granted, &host->h_reclaim); list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { list_del_init(&fl->fl_u.nfs_fl.list); + /* Why are we leaking memory here? --okir */ if (signalled()) continue; if (nlmclnt_reclaim(host, fl) != 0) @@ -218,11 +200,13 @@ restart: list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); if (host->h_nsmstate != nsmstate) { /* Argh! The server rebooted again! */ - list_splice_init(&host->h_granted, &host->h_reclaim); goto restart; } } - nlmclnt_finish_reclaim(host); + + host->h_reclaiming = 0; + up_write(&host->h_rwsem); + dprintk("NLM: done reclaiming locks for host %s", host->h_name); /* Now, wake up all processes that sleep on a blocked lock */ list_for_each_entry(block, &nlm_blocked, b_list) { diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 0116729cec..3d84f600b6 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -36,14 +36,14 @@ static const struct rpc_call_ops nlmclnt_cancel_ops; /* * Cookie counter for NLM requests */ -static u32 nlm_cookie = 0x1234; +static atomic_t nlm_cookie = ATOMIC_INIT(0x1234); -static inline void nlmclnt_next_cookie(struct nlm_cookie *c) +void nlmclnt_next_cookie(struct nlm_cookie *c) { - memcpy(c->data, &nlm_cookie, 4); - memset(c->data+4, 0, 4); + u32 cookie = atomic_inc_return(&nlm_cookie); + + memcpy(c->data, &cookie, 4); c->len=4; - nlm_cookie++; } static struct nlm_lockowner *nlm_get_lockowner(struct nlm_lockowner *lockowner) @@ -153,6 +153,7 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) { struct rpc_clnt *client = NFS_CLIENT(inode); struct sockaddr_in addr; + struct nfs_server *nfssrv = NFS_SERVER(inode); struct nlm_host *host; struct nlm_rqst *call; sigset_t oldset; @@ -166,7 +167,9 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) } rpc_peeraddr(client, (struct sockaddr *) &addr, sizeof(addr)); - host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers); + host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers, + nfssrv->nfs_client->cl_hostname, + strlen(nfssrv->nfs_client->cl_hostname)); if (host == NULL) return -ENOLCK; @@ -499,7 +502,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) unsigned char fl_flags = fl->fl_flags; int status = -ENOLCK; - if (!host->h_monitored && nsm_monitor(host) < 0) { + if (nsm_monitor(host) < 0) { printk(KERN_NOTICE "lockd: failed to monitor %s\n", host->h_name); goto out; diff --git a/fs/lockd/host.c b/fs/lockd/host.c index a0d0b58ce7..fb24a97303 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -27,46 +27,60 @@ #define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ) #define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ) -static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; +static struct hlist_head nlm_hosts[NLM_HOST_NRHASH]; static unsigned long next_gc; static int nrhosts; static DEFINE_MUTEX(nlm_host_mutex); static void nlm_gc_hosts(void); +static struct nsm_handle * __nsm_find(const struct sockaddr_in *, + const char *, int, int); /* * Find an NLM server handle in the cache. If there is none, create it. */ struct nlm_host * -nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version) +nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version, + const char *hostname, int hostname_len) { - return nlm_lookup_host(0, sin, proto, version); + return nlm_lookup_host(0, sin, proto, version, + hostname, hostname_len); } /* * Find an NLM client handle in the cache. If there is none, create it. */ struct nlm_host * -nlmsvc_lookup_host(struct svc_rqst *rqstp) +nlmsvc_lookup_host(struct svc_rqst *rqstp, + const char *hostname, int hostname_len) { return nlm_lookup_host(1, &rqstp->rq_addr, - rqstp->rq_prot, rqstp->rq_vers); + rqstp->rq_prot, rqstp->rq_vers, + hostname, hostname_len); } /* * Common host lookup routine for server & client */ struct nlm_host * -nlm_lookup_host(int server, struct sockaddr_in *sin, - int proto, int version) +nlm_lookup_host(int server, const struct sockaddr_in *sin, + int proto, int version, + const char *hostname, + int hostname_len) { - struct nlm_host *host, **hp; - u32 addr; + struct hlist_head *chain; + struct hlist_node *pos; + struct nlm_host *host; + struct nsm_handle *nsm = NULL; int hash; - dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n", - (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version); + dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n", + NIPQUAD(sin->sin_addr.s_addr), proto, version, + server? "server" : "client", + hostname_len, + hostname? hostname : ""); + hash = NLM_ADDRHASH(sin->sin_addr.s_addr); @@ -76,7 +90,22 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, if (time_after_eq(jiffies, next_gc)) nlm_gc_hosts(); - for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { + /* We may keep several nlm_host objects for a peer, because each + * nlm_host is identified by + * (address, protocol, version, server/client) + * We could probably simplify this a little by putting all those + * different NLM rpc_clients into one single nlm_host object. + * This would allow us to have one nlm_host per address. + */ + chain = &nlm_hosts[hash]; + hlist_for_each_entry(host, pos, chain, h_hash) { + if (!nlm_cmp_addr(&host->h_addr, sin)) + continue; + + /* See if we have an NSM handle for this client */ + if (!nsm) + nsm = host->h_nsmhandle; + if (host->h_proto != proto) continue; if (host->h_version != version) @@ -84,28 +113,30 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, if (host->h_server != server) continue; - if (nlm_cmp_addr(&host->h_addr, sin)) { - if (hp != nlm_hosts + hash) { - *hp = host->h_next; - host->h_next = nlm_hosts[hash]; - nlm_hosts[hash] = host; - } - nlm_get_host(host); - mutex_unlock(&nlm_host_mutex); - return host; - } - } + /* Move to head of hash chain. */ + hlist_del(&host->h_hash); + hlist_add_head(&host->h_hash, chain); - /* Ooops, no host found, create it */ - dprintk("lockd: creating host entry\n"); + nlm_get_host(host); + goto out; + } + if (nsm) + atomic_inc(&nsm->sm_count); - host = kzalloc(sizeof(*host), GFP_KERNEL); - if (!host) - goto nohost; + host = NULL; - addr = sin->sin_addr.s_addr; - sprintf(host->h_name, "%u.%u.%u.%u", NIPQUAD(addr)); + /* Sadly, the host isn't in our hash table yet. See if + * we have an NSM handle for it. If not, create one. + */ + if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len))) + goto out; + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) { + nsm_release(nsm); + goto out; + } + host->h_name = nsm->sm_name; host->h_addr = *sin; host->h_addr.sin_port = 0; /* ouch! */ host->h_version = version; @@ -119,9 +150,9 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, init_rwsem(&host->h_rwsem); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ + host->h_nsmhandle = nsm; host->h_server = server; - host->h_next = nlm_hosts[hash]; - nlm_hosts[hash] = host; + hlist_add_head(&host->h_hash, chain); INIT_LIST_HEAD(&host->h_lockowners); spin_lock_init(&host->h_lock); INIT_LIST_HEAD(&host->h_granted); @@ -130,35 +161,39 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, if (++nrhosts > NLM_HOST_MAX) next_gc = 0; -nohost: +out: mutex_unlock(&nlm_host_mutex); return host; } -struct nlm_host * -nlm_find_client(void) +/* + * Destroy a host + */ +static void +nlm_destroy_host(struct nlm_host *host) { - /* find a nlm_host for a client for which h_killed == 0. - * and return it + struct rpc_clnt *clnt; + + BUG_ON(!list_empty(&host->h_lockowners)); + BUG_ON(atomic_read(&host->h_count)); + + /* + * Release NSM handle and unmonitor host. */ - int hash; - mutex_lock(&nlm_host_mutex); - for (hash = 0 ; hash < NLM_HOST_NRHASH; hash++) { - struct nlm_host *host, **hp; - for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { - if (host->h_server && - host->h_killed == 0) { - nlm_get_host(host); - mutex_unlock(&nlm_host_mutex); - return host; - } + nsm_unmonitor(host); + + if ((clnt = host->h_rpcclnt) != NULL) { + if (atomic_read(&clnt->cl_users)) { + printk(KERN_WARNING + "lockd: active RPC handle\n"); + clnt->cl_dead = 1; + } else { + rpc_destroy_client(host->h_rpcclnt); } } - mutex_unlock(&nlm_host_mutex); - return NULL; + kfree(host); } - /* * Create the NLM RPC client for an NLM peer */ @@ -259,6 +294,65 @@ void nlm_release_host(struct nlm_host *host) } } +/* + * We were notified that the host indicated by address &sin + * has rebooted. + * Release all resources held by that peer. + */ +void nlm_host_rebooted(const struct sockaddr_in *sin, + const char *hostname, int hostname_len, + u32 new_state) +{ + struct hlist_head *chain; + struct hlist_node *pos; + struct nsm_handle *nsm; + struct nlm_host *host; + + dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n", + hostname, NIPQUAD(sin->sin_addr)); + + /* Find the NSM handle for this peer */ + if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0))) + return; + + /* When reclaiming locks on this peer, make sure that + * we set up a new notification */ + nsm->sm_monitored = 0; + + /* Mark all hosts tied to this NSM state as having rebooted. + * We run the loop repeatedly, because we drop the host table + * lock for this. + * To avoid processing a host several times, we match the nsmstate. + */ +again: mutex_lock(&nlm_host_mutex); + for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { + hlist_for_each_entry(host, pos, chain, h_hash) { + if (host->h_nsmhandle == nsm + && host->h_nsmstate != new_state) { + host->h_nsmstate = new_state; + host->h_state++; + + nlm_get_host(host); + mutex_unlock(&nlm_host_mutex); + + if (host->h_server) { + /* We're server for this guy, just ditch + * all the locks he held. */ + nlmsvc_free_host_resources(host); + } else { + /* He's the server, initiate lock recovery. */ + nlmclnt_recovery(host); + } + + nlm_release_host(host); + goto again; + } + } + } + + mutex_unlock(&nlm_host_mutex); +} + /* * Shut down the hosts module. * Note that this routine is called only at server shutdown time. @@ -266,16 +360,17 @@ void nlm_release_host(struct nlm_host *host) void nlm_shutdown_hosts(void) { + struct hlist_head *chain; + struct hlist_node *pos; struct nlm_host *host; - int i; dprintk("lockd: shutting down host module\n"); mutex_lock(&nlm_host_mutex); /* First, make all hosts eligible for gc */ dprintk("lockd: nuking all hosts...\n"); - for (i = 0; i < NLM_HOST_NRHASH; i++) { - for (host = nlm_hosts[i]; host; host = host->h_next) + for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { + hlist_for_each_entry(host, pos, chain, h_hash) host->h_expires = jiffies - 1; } @@ -287,8 +382,8 @@ nlm_shutdown_hosts(void) if (nrhosts) { printk(KERN_WARNING "lockd: couldn't shutdown host module!\n"); dprintk("lockd: %d hosts left:\n", nrhosts); - for (i = 0; i < NLM_HOST_NRHASH; i++) { - for (host = nlm_hosts[i]; host; host = host->h_next) { + for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { + hlist_for_each_entry(host, pos, chain, h_hash) { dprintk(" %s (cnt %d use %d exp %ld)\n", host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires); @@ -305,45 +400,32 @@ nlm_shutdown_hosts(void) static void nlm_gc_hosts(void) { - struct nlm_host **q, *host; - struct rpc_clnt *clnt; - int i; + struct hlist_head *chain; + struct hlist_node *pos, *next; + struct nlm_host *host; dprintk("lockd: host garbage collection\n"); - for (i = 0; i < NLM_HOST_NRHASH; i++) { - for (host = nlm_hosts[i]; host; host = host->h_next) + for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { + hlist_for_each_entry(host, pos, chain, h_hash) host->h_inuse = 0; } /* Mark all hosts that hold locks, blocks or shares */ nlmsvc_mark_resources(); - for (i = 0; i < NLM_HOST_NRHASH; i++) { - q = &nlm_hosts[i]; - while ((host = *q) != NULL) { + for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { + hlist_for_each_entry_safe(host, pos, next, chain, h_hash) { if (atomic_read(&host->h_count) || host->h_inuse || time_before(jiffies, host->h_expires)) { dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n", host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires); - q = &host->h_next; continue; } dprintk("lockd: delete host %s\n", host->h_name); - *q = host->h_next; - /* Don't unmonitor hosts that have been invalidated */ - if (host->h_monitored && !host->h_killed) - nsm_unmonitor(host); - if ((clnt = host->h_rpcclnt) != NULL) { - if (atomic_read(&clnt->cl_users)) { - printk(KERN_WARNING - "lockd: active RPC handle\n"); - clnt->cl_dead = 1; - } else { - rpc_destroy_client(host->h_rpcclnt); - } - } - kfree(host); + hlist_del_init(&host->h_hash); + + nlm_destroy_host(host); nrhosts--; } } @@ -351,3 +433,88 @@ nlm_gc_hosts(void) next_gc = jiffies + NLM_HOST_COLLECT; } + +/* + * Manage NSM handles + */ +static LIST_HEAD(nsm_handles); +static DEFINE_MUTEX(nsm_mutex); + +static struct nsm_handle * +__nsm_find(const struct sockaddr_in *sin, + const char *hostname, int hostname_len, + int create) +{ + struct nsm_handle *nsm = NULL; + struct list_head *pos; + + if (!sin) + return NULL; + + if (hostname && memchr(hostname, '/', hostname_len) != NULL) { + if (printk_ratelimit()) { + printk(KERN_WARNING "Invalid hostname \"%.*s\" " + "in NFS lock request\n", + hostname_len, hostname); + } + return NULL; + } + + mutex_lock(&nsm_mutex); + list_for_each(pos, &nsm_handles) { + nsm = list_entry(pos, struct nsm_handle, sm_link); + + if (hostname && nsm_use_hostnames) { + if (strlen(nsm->sm_name) != hostname_len + || memcmp(nsm->sm_name, hostname, hostname_len)) + continue; + } else if (!nlm_cmp_addr(&nsm->sm_addr, sin)) + continue; + atomic_inc(&nsm->sm_count); + goto out; + } + + if (!create) { + nsm = NULL; + goto out; + } + + nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); + if (nsm != NULL) { + nsm->sm_addr = *sin; + nsm->sm_name = (char *) (nsm + 1); + memcpy(nsm->sm_name, hostname, hostname_len); + nsm->sm_name[hostname_len] = '\0'; + atomic_set(&nsm->sm_count, 1); + + list_add(&nsm->sm_link, &nsm_handles); + } + +out: + mutex_unlock(&nsm_mutex); + return nsm; +} + +struct nsm_handle * +nsm_find(const struct sockaddr_in *sin, const char *hostname, int hostname_len) +{ + return __nsm_find(sin, hostname, hostname_len, 1); +} + +/* + * Release an NSM handle + */ +void +nsm_release(struct nsm_handle *nsm) +{ + if (!nsm) + return; + if (atomic_dec_and_test(&nsm->sm_count)) { + mutex_lock(&nsm_mutex); + if (atomic_read(&nsm->sm_count) == 0) { + list_del(&nsm->sm_link); + kfree(nsm); + } + mutex_unlock(&nsm_mutex); + } +} diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index a816b920d4..e0179f8c32 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -24,13 +24,13 @@ static struct rpc_program nsm_program; /* * Local NSM state */ -u32 nsm_local_state; +int nsm_local_state; /* * Common procedure for SM_MON/SM_UNMON calls */ static int -nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) +nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) { struct rpc_clnt *clnt; int status; @@ -46,10 +46,11 @@ nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) goto out; } - args.addr = host->h_addr.sin_addr.s_addr; - args.proto= (host->h_proto<<1) | host->h_server; + memset(&args, 0, sizeof(args)); + args.mon_name = nsm->sm_name; + args.addr = nsm->sm_addr.sin_addr.s_addr; args.prog = NLM_PROGRAM; - args.vers = host->h_version; + args.vers = 3; args.proc = NLMPROC_NSM_NOTIFY; memset(res, 0, sizeof(*res)); @@ -70,17 +71,22 @@ nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) int nsm_monitor(struct nlm_host *host) { + struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_res res; int status; dprintk("lockd: nsm_monitor(%s)\n", host->h_name); + BUG_ON(nsm == NULL); - status = nsm_mon_unmon(host, SM_MON, &res); + if (nsm->sm_monitored) + return 0; + + status = nsm_mon_unmon(nsm, SM_MON, &res); if (status < 0 || res.status != 0) printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name); else - host->h_monitored = 1; + nsm->sm_monitored = 1; return status; } @@ -90,16 +96,26 @@ nsm_monitor(struct nlm_host *host) int nsm_unmonitor(struct nlm_host *host) { + struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_res res; - int status; - - dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name); - - status = nsm_mon_unmon(host, SM_UNMON, &res); - if (status < 0) - printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", host->h_name); - else - host->h_monitored = 0; + int status = 0; + + if (nsm == NULL) + return 0; + host->h_nsmhandle = NULL; + + if (atomic_read(&nsm->sm_count) == 1 + && nsm->sm_monitored && !nsm->sm_sticky) { + dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name); + + status = nsm_mon_unmon(nsm, SM_UNMON, &res); + if (status < 0) + printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", + host->h_name); + else + nsm->sm_monitored = 0; + } + nsm_release(nsm); return status; } @@ -135,7 +151,7 @@ nsm_create(void) static u32 * xdr_encode_common(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) { - char buffer[20]; + char buffer[20], *name; /* * Use the dotted-quad IP address of the remote host as @@ -143,8 +159,13 @@ xdr_encode_common(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) * hostname first for whatever remote hostname it receives, * so this works alright. */ - sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr)); - if (!(p = xdr_encode_string(p, buffer)) + if (nsm_use_hostnames) { + name = argp->mon_name; + } else { + sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr)); + name = buffer; + } + if (!(p = xdr_encode_string(p, name)) || !(p = xdr_encode_string(p, utsname()->nodename))) return ERR_PTR(-EIO); *p++ = htonl(argp->prog); @@ -160,9 +181,11 @@ xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) p = xdr_encode_common(rqstp, p, argp); if (IS_ERR(p)) return PTR_ERR(p); + + /* Surprise - there may even be room for an IPv6 address now */ *p++ = argp->addr; - *p++ = argp->vers; - *p++ = argp->proto; + *p++ = 0; + *p++ = 0; *p++ = 0; rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); return 0; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 3cc369e569..634139232a 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #define NLMDBG_FACILITY NLMDBG_SVC @@ -61,6 +62,7 @@ static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); static unsigned long nlm_grace_period; static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; static int nlm_udpport, nlm_tcpport; +int nsm_use_hostnames = 0; /* * Constants needed for the sysctl interface. @@ -395,6 +397,22 @@ static ctl_table nlm_sysctls[] = { .extra1 = (int *) &nlm_port_min, .extra2 = (int *) &nlm_port_max, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nsm_use_hostnames", + .data = &nsm_use_hostnames, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nsm_local_state", + .data = &nsm_local_state, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0 } }; @@ -483,6 +501,7 @@ module_param_call(nlm_udpport, param_set_port, param_get_int, &nlm_udpport, 0644); module_param_call(nlm_tcpport, param_set_port, param_get_int, &nlm_tcpport, 0644); +module_param(nsm_use_hostnames, bool, 0644); /* * Initialising and terminating the module. diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index a2dd9ccb9b..fa370f6eb0 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -38,8 +38,8 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, return nlm_lck_denied_nolocks; /* Obtain host handle */ - if (!(host = nlmsvc_lookup_host(rqstp)) - || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0)) + if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) + || (argp->monitor && nsm_monitor(host) < 0)) goto no_locks; *hostp = host; @@ -260,7 +260,9 @@ static int nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *a struct nlm_rqst *call; int stat; - host = nlmsvc_lookup_host(rqstp); + host = nlmsvc_lookup_host(rqstp, + argp->lock.caller, + argp->lock.len); if (host == NULL) return rpc_system_err; @@ -420,10 +422,6 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { struct sockaddr_in saddr = rqstp->rq_addr; - int vers = argp->vers; - int prot = argp->proto >> 1; - - struct nlm_host *host; dprintk("lockd: SM_NOTIFY called\n"); if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) @@ -438,21 +436,10 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, /* Obtain the host pointer for this NFS server and try to * reclaim all locks we hold on this server. */ + memset(&saddr, 0, sizeof(saddr)); saddr.sin_addr.s_addr = argp->addr; + nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state); - if ((argp->proto & 1)==0) { - if ((host = nlmclnt_lookup_host(&saddr, prot, vers)) != NULL) { - nlmclnt_recovery(host, argp->state); - nlm_release_host(host); - } - } else { - /* If we run on an NFS server, delete all locks held by the client */ - - if ((host = nlm_lookup_host(1, &saddr, prot, vers)) != NULL) { - nlmsvc_free_host_resources(host); - nlm_release_host(host); - } - } return rpc_success; } @@ -468,7 +455,7 @@ nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, dprintk("lockd: GRANTED_RES called\n"); - nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status); + nlmsvc_grant_reply(&argp->cookie, argp->status); return rpc_success; } diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 93c00ee718..814c6064c9 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -40,7 +40,7 @@ static void nlmsvc_release_block(struct nlm_block *block); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); -static int nlmsvc_remove_block(struct nlm_block *block); +static void nlmsvc_remove_block(struct nlm_block *block); static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock); static void nlmsvc_freegrantargs(struct nlm_rqst *call); @@ -49,7 +49,7 @@ static const struct rpc_call_ops nlmsvc_grant_ops; /* * The list of blocked locks to retry */ -static struct nlm_block * nlm_blocked; +static LIST_HEAD(nlm_blocked); /* * Insert a blocked lock into the global list @@ -57,48 +57,44 @@ static struct nlm_block * nlm_blocked; static void nlmsvc_insert_block(struct nlm_block *block, unsigned long when) { - struct nlm_block **bp, *b; + struct nlm_block *b; + struct list_head *pos; dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when); - kref_get(&block->b_count); - if (block->b_queued) - nlmsvc_remove_block(block); - bp = &nlm_blocked; + if (list_empty(&block->b_list)) { + kref_get(&block->b_count); + } else { + list_del_init(&block->b_list); + } + + pos = &nlm_blocked; if (when != NLM_NEVER) { if ((when += jiffies) == NLM_NEVER) when ++; - while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER) - bp = &b->b_next; - } else - while ((b = *bp) != 0) - bp = &b->b_next; + list_for_each(pos, &nlm_blocked) { + b = list_entry(pos, struct nlm_block, b_list); + if (time_after(b->b_when,when) || b->b_when == NLM_NEVER) + break; + } + /* On normal exit from the loop, pos == &nlm_blocked, + * so we will be adding to the end of the list - good + */ + } - block->b_queued = 1; + list_add_tail(&block->b_list, pos); block->b_when = when; - block->b_next = b; - *bp = block; } /* * Remove a block from the global list */ -static int +static inline void nlmsvc_remove_block(struct nlm_block *block) { - struct nlm_block **bp, *b; - - if (!block->b_queued) - return 1; - for (bp = &nlm_blocked; (b = *bp) != 0; bp = &b->b_next) { - if (b == block) { - *bp = block->b_next; - block->b_queued = 0; - nlmsvc_release_block(block); - return 1; - } + if (!list_empty(&block->b_list)) { + list_del_init(&block->b_list); + nlmsvc_release_block(block); } - - return 0; } /* @@ -107,14 +103,14 @@ nlmsvc_remove_block(struct nlm_block *block) static struct nlm_block * nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock) { - struct nlm_block **head, *block; + struct nlm_block *block; struct file_lock *fl; dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n", file, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end, lock->fl.fl_type); - for (head = &nlm_blocked; (block = *head) != 0; head = &block->b_next) { + list_for_each_entry(block, &nlm_blocked, b_list) { fl = &block->b_call->a_args.lock.fl; dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n", block->b_file, fl->fl_pid, @@ -143,20 +139,20 @@ static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b) * Find a block with a given NLM cookie. */ static inline struct nlm_block * -nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin) +nlmsvc_find_block(struct nlm_cookie *cookie) { struct nlm_block *block; - for (block = nlm_blocked; block; block = block->b_next) { - dprintk("cookie: head of blocked queue %p, block %p\n", - nlm_blocked, block); - if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie) - && nlm_cmp_addr(sin, &block->b_host->h_addr)) - break; + list_for_each_entry(block, &nlm_blocked, b_list) { + if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie)) + goto found; } - if (block != NULL) - kref_get(&block->b_count); + return NULL; + +found: + dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block); + kref_get(&block->b_count); return block; } @@ -169,6 +165,11 @@ nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin) * request, but (as I found out later) that's because some implementations * do just this. Never mind the standards comittees, they support our * logging industries. + * + * 10 years later: I hope we can safely ignore these old and broken + * clients by now. Let's fix this so we can uniquely identify an incoming + * GRANTED_RES message by cookie, without having to rely on the client's IP + * address. --okir */ static inline struct nlm_block * nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, @@ -179,7 +180,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_rqst *call = NULL; /* Create host handle for callback */ - host = nlmsvc_lookup_host(rqstp); + host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); if (host == NULL) return NULL; @@ -192,6 +193,8 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, if (block == NULL) goto failed; kref_init(&block->b_count); + INIT_LIST_HEAD(&block->b_list); + INIT_LIST_HEAD(&block->b_flist); if (!nlmsvc_setgrantargs(call, lock)) goto failed_free; @@ -199,7 +202,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, /* Set notifier function for VFS, and init args */ call->a_args.lock.fl.fl_flags |= FL_SLEEP; call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations; - call->a_args.cookie = *cookie; /* see above */ + nlmclnt_next_cookie(&call->a_args.cookie); dprintk("lockd: created block %p...\n", block); @@ -210,8 +213,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, file->f_count++; /* Add to file's list of blocks */ - block->b_fnext = file->f_blocks; - file->f_blocks = block; + list_add(&block->b_flist, &file->f_blocks); /* Set up RPC arguments for callback */ block->b_call = call; @@ -248,19 +250,13 @@ static void nlmsvc_free_block(struct kref *kref) { struct nlm_block *block = container_of(kref, struct nlm_block, b_count); struct nlm_file *file = block->b_file; - struct nlm_block **bp; dprintk("lockd: freeing block %p...\n", block); - down(&file->f_sema); /* Remove block from file's list of blocks */ - for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) { - if (*bp == block) { - *bp = block->b_fnext; - break; - } - } - up(&file->f_sema); + mutex_lock(&file->f_mutex); + list_del_init(&block->b_flist); + mutex_unlock(&file->f_mutex); nlmsvc_freegrantargs(block->b_call); nlm_release_call(block->b_call); @@ -274,47 +270,32 @@ static void nlmsvc_release_block(struct nlm_block *block) kref_put(&block->b_count, nlmsvc_free_block); } -static void nlmsvc_act_mark(struct nlm_host *host, struct nlm_file *file) -{ - struct nlm_block *block; - - down(&file->f_sema); - for (block = file->f_blocks; block != NULL; block = block->b_fnext) - block->b_host->h_inuse = 1; - up(&file->f_sema); -} - -static void nlmsvc_act_unlock(struct nlm_host *host, struct nlm_file *file) +/* + * Loop over all blocks and delete blocks held by + * a matching host. + */ +void nlmsvc_traverse_blocks(struct nlm_host *host, + struct nlm_file *file, + nlm_host_match_fn_t match) { - struct nlm_block *block; + struct nlm_block *block, *next; restart: - down(&file->f_sema); - for (block = file->f_blocks; block != NULL; block = block->b_fnext) { - if (host != NULL && host != block->b_host) + mutex_lock(&file->f_mutex); + list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) { + if (!match(block->b_host, host)) continue; - if (!block->b_queued) + /* Do not destroy blocks that are not on + * the global retry list - why? */ + if (list_empty(&block->b_list)) continue; kref_get(&block->b_count); - up(&file->f_sema); + mutex_unlock(&file->f_mutex); nlmsvc_unlink_block(block); nlmsvc_release_block(block); goto restart; } - up(&file->f_sema); -} - -/* - * Loop over all blocks and perform the action specified. - * (NLM_ACT_CHECK handled by nlmsvc_inspect_file). - */ -void -nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) -{ - if (action == NLM_ACT_MARK) - nlmsvc_act_mark(host, file); - else - nlmsvc_act_unlock(host, file); + mutex_unlock(&file->f_mutex); } /* @@ -373,7 +354,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, lock->fl.fl_flags &= ~FL_SLEEP; again: /* Lock file against concurrent access */ - down(&file->f_sema); + mutex_lock(&file->f_mutex); /* Get existing block (in case client is busy-waiting) */ block = nlmsvc_lookup_block(file, lock); if (block == NULL) { @@ -411,10 +392,10 @@ again: /* If we don't have a block, create and initialize it. Then * retry because we may have slept in kmalloc. */ - /* We have to release f_sema as nlmsvc_create_block may try to + /* We have to release f_mutex as nlmsvc_create_block may try to * to claim it while doing host garbage collection */ if (newblock == NULL) { - up(&file->f_sema); + mutex_unlock(&file->f_mutex); dprintk("lockd: blocking on this lock (allocating).\n"); if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie))) return nlm_lck_denied_nolocks; @@ -424,7 +405,7 @@ again: /* Append to list of blocked */ nlmsvc_insert_block(newblock, NLM_NEVER); out: - up(&file->f_sema); + mutex_unlock(&file->f_mutex); nlmsvc_release_block(newblock); nlmsvc_release_block(block); dprintk("lockd: nlmsvc_lock returned %u\n", ret); @@ -451,6 +432,7 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, (long long)conflock->fl.fl_start, (long long)conflock->fl.fl_end); conflock->caller = "somehost"; /* FIXME */ + conflock->len = strlen(conflock->caller); conflock->oh.len = 0; /* don't return OH info */ conflock->svid = conflock->fl.fl_pid; return nlm_lck_denied; @@ -507,9 +489,9 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - down(&file->f_sema); + mutex_lock(&file->f_mutex); block = nlmsvc_lookup_block(file, lock); - up(&file->f_sema); + mutex_unlock(&file->f_mutex); if (block != NULL) { status = nlmsvc_unlink_block(block); nlmsvc_release_block(block); @@ -527,10 +509,10 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) static void nlmsvc_notify_blocked(struct file_lock *fl) { - struct nlm_block **bp, *block; + struct nlm_block *block; dprintk("lockd: VFS unblock notification for block %p\n", fl); - for (bp = &nlm_blocked; (block = *bp) != 0; bp = &block->b_next) { + list_for_each_entry(block, &nlm_blocked, b_list) { if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) { nlmsvc_insert_block(block, 0); svc_wake_up(block->b_daemon); @@ -663,17 +645,14 @@ static const struct rpc_call_ops nlmsvc_grant_ops = { * block. */ void -nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status) +nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status) { struct nlm_block *block; - struct nlm_file *file; - dprintk("grant_reply: looking for cookie %x, host (%08x), s=%d \n", - *(unsigned int *)(cookie->data), - ntohl(rqstp->rq_addr.sin_addr.s_addr), status); - if (!(block = nlmsvc_find_block(cookie, &rqstp->rq_addr))) + dprintk("grant_reply: looking for cookie %x, s=%d \n", + *(unsigned int *)(cookie->data), status); + if (!(block = nlmsvc_find_block(cookie))) return; - file = block->b_file; if (block) { if (status == NLM_LCK_DENIED_GRACE_PERIOD) { @@ -696,16 +675,19 @@ nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status unsigned long nlmsvc_retry_blocked(void) { - struct nlm_block *block; + unsigned long timeout = MAX_SCHEDULE_TIMEOUT; + struct nlm_block *block; + + while (!list_empty(&nlm_blocked)) { + block = list_entry(nlm_blocked.next, struct nlm_block, b_list); - dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", - nlm_blocked, - nlm_blocked? nlm_blocked->b_when : 0); - while ((block = nlm_blocked) != 0) { if (block->b_when == NLM_NEVER) break; - if (time_after(block->b_when,jiffies)) + if (time_after(block->b_when,jiffies)) { + timeout = block->b_when - jiffies; break; + } + dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", block, block->b_when); kref_get(&block->b_count); @@ -713,8 +695,5 @@ nlmsvc_retry_blocked(void) nlmsvc_release_block(block); } - if ((block = nlm_blocked) && block->b_when != NLM_NEVER) - return (block->b_when - jiffies); - - return MAX_SCHEDULE_TIMEOUT; + return timeout; } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index dbb66a3b5c..75b2c81bcb 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -66,8 +66,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, return nlm_lck_denied_nolocks; /* Obtain host handle */ - if (!(host = nlmsvc_lookup_host(rqstp)) - || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0)) + if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) + || (argp->monitor && nsm_monitor(host) < 0)) goto no_locks; *hostp = host; @@ -287,7 +287,9 @@ static int nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *ar struct nlm_rqst *call; int stat; - host = nlmsvc_lookup_host(rqstp); + host = nlmsvc_lookup_host(rqstp, + argp->lock.caller, + argp->lock.len); if (host == NULL) return rpc_system_err; @@ -449,9 +451,6 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { struct sockaddr_in saddr = rqstp->rq_addr; - int vers = argp->vers; - int prot = argp->proto >> 1; - struct nlm_host *host; dprintk("lockd: SM_NOTIFY called\n"); if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) @@ -466,19 +465,9 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, /* Obtain the host pointer for this NFS server and try to * reclaim all locks we hold on this server. */ + memset(&saddr, 0, sizeof(saddr)); saddr.sin_addr.s_addr = argp->addr; - if ((argp->proto & 1)==0) { - if ((host = nlmclnt_lookup_host(&saddr, prot, vers)) != NULL) { - nlmclnt_recovery(host, argp->state); - nlm_release_host(host); - } - } else { - /* If we run on an NFS server, delete all locks held by the client */ - if ((host = nlm_lookup_host(1, &saddr, prot, vers)) != NULL) { - nlmsvc_free_host_resources(host); - nlm_release_host(host); - } - } + nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state); return rpc_success; } @@ -495,7 +484,7 @@ nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, dprintk("lockd: GRANTED_RES called\n"); - nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status); + nlmsvc_grant_reply(&argp->cookie, argp->status); return rpc_success; } diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 27288c83da..b9926ce878 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -85,24 +85,20 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, } /* - * Traverse all shares for a given file (and host). - * NLM_ACT_CHECK is handled by nlmsvc_inspect_file. + * Traverse all shares for a given file, and delete + * those owned by the given (type of) host */ -void -nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action) +void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, + nlm_host_match_fn_t match) { struct nlm_share *share, **shpp; shpp = &file->f_shares; while ((share = *shpp) != NULL) { - if (action == NLM_ACT_MARK) - share->s_host->h_inuse = 1; - else if (action == NLM_ACT_UNLOCK) { - if (host == NULL || host == share->s_host) { - *shpp = share->s_next; - kfree(share); - continue; - } + if (match(share->s_host, host)) { + *shpp = share->s_next; + kfree(share); + continue; } shpp = &share->s_next; } diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index a92dd98f84..514f5f2070 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -25,9 +25,9 @@ /* * Global file hash table */ -#define FILE_HASH_BITS 5 +#define FILE_HASH_BITS 7 #define FILE_NRHASH (1<f_next) + hlist_for_each_entry(file, pos, &nlm_files[hash], f_list) if (!nfs_compare_fh(&file->f_handle, f)) goto found; @@ -105,8 +106,9 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, goto out_unlock; memcpy(&file->f_handle, f, sizeof(struct nfs_fh)); - file->f_hash = hash; - init_MUTEX(&file->f_sema); + mutex_init(&file->f_mutex); + INIT_HLIST_NODE(&file->f_list); + INIT_LIST_HEAD(&file->f_blocks); /* Open the file. Note that this must not sleep for too long, else * we would lock up lockd:-) So no NFS re-exports, folks. @@ -115,12 +117,11 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, * the file. */ if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) { - dprintk("lockd: open failed (nfserr %d)\n", ntohl(nfserr)); + dprintk("lockd: open failed (error %d)\n", nfserr); goto out_free; } - file->f_next = nlm_files[hash]; - nlm_files[hash] = file; + hlist_add_head(&file->f_list, &nlm_files[hash]); found: dprintk("lockd: found file %p (count %d)\n", file, file->f_count); @@ -149,22 +150,14 @@ out_free: static inline void nlm_delete_file(struct nlm_file *file) { - struct nlm_file **fp, *f; - nlm_debug_print_file("closing file", file); - - fp = nlm_files + file->f_hash; - while ((f = *fp) != NULL) { - if (f == file) { - *fp = file->f_next; - nlmsvc_ops->fclose(file->f_file); - kfree(file); - return; - } - fp = &f->f_next; + if (!hlist_unhashed(&file->f_list)) { + hlist_del(&file->f_list); + nlmsvc_ops->fclose(file->f_file); + kfree(file); + } else { + printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); } - - printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); } /* @@ -172,7 +165,8 @@ nlm_delete_file(struct nlm_file *file) * action. */ static int -nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) +nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, + nlm_host_match_fn_t match) { struct inode *inode = nlmsvc_file_inode(file); struct file_lock *fl; @@ -186,17 +180,11 @@ again: /* update current lock count */ file->f_locks++; + lockhost = (struct nlm_host *) fl->fl_owner; - if (action == NLM_ACT_MARK) - lockhost->h_inuse = 1; - else if (action == NLM_ACT_CHECK) - return 1; - else if (action == NLM_ACT_UNLOCK) { + if (match(lockhost, host)) { struct file_lock lock = *fl; - if (host && lockhost != host) - continue; - lock.fl_type = F_UNLCK; lock.fl_start = 0; lock.fl_end = OFFSET_MAX; @@ -213,53 +201,66 @@ again: } /* - * Operate on a single file + * Inspect a single file */ static inline int -nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) +nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match) { - if (action == NLM_ACT_CHECK) { - /* Fast path for mark and sweep garbage collection */ - if (file->f_count || file->f_blocks || file->f_shares) + nlmsvc_traverse_blocks(host, file, match); + nlmsvc_traverse_shares(host, file, match); + return nlm_traverse_locks(host, file, match); +} + +/* + * Quick check whether there are still any locks, blocks or + * shares on a given file. + */ +static inline int +nlm_file_inuse(struct nlm_file *file) +{ + struct inode *inode = nlmsvc_file_inode(file); + struct file_lock *fl; + + if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares) + return 1; + + for (fl = inode->i_flock; fl; fl = fl->fl_next) { + if (fl->fl_lmops == &nlmsvc_lock_operations) return 1; - } else { - nlmsvc_traverse_blocks(host, file, action); - nlmsvc_traverse_shares(host, file, action); } - return nlm_traverse_locks(host, file, action); + file->f_locks = 0; + return 0; } /* * Loop over all files in the file table. */ static int -nlm_traverse_files(struct nlm_host *host, int action) +nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) { - struct nlm_file *file, **fp; + struct hlist_node *pos, *next; + struct nlm_file *file; int i, ret = 0; mutex_lock(&nlm_file_mutex); for (i = 0; i < FILE_NRHASH; i++) { - fp = nlm_files + i; - while ((file = *fp) != NULL) { + hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) { file->f_count++; mutex_unlock(&nlm_file_mutex); /* Traverse locks, blocks and shares of this file * and update file->f_locks count */ - if (nlm_inspect_file(host, file, action)) + if (nlm_inspect_file(host, file, match)) ret = 1; mutex_lock(&nlm_file_mutex); file->f_count--; /* No more references to this file. Let go of it. */ - if (!file->f_blocks && !file->f_locks + if (list_empty(&file->f_blocks) && !file->f_locks && !file->f_shares && !file->f_count) { - *fp = file->f_next; + hlist_del(&file->f_list); nlmsvc_ops->fclose(file->f_file); kfree(file); - } else { - fp = &file->f_next; } } } @@ -286,14 +287,46 @@ nlm_release_file(struct nlm_file *file) mutex_lock(&nlm_file_mutex); /* If there are no more locks etc, delete the file */ - if(--file->f_count == 0) { - if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) - nlm_delete_file(file); - } + if (--file->f_count == 0 && !nlm_file_inuse(file)) + nlm_delete_file(file); mutex_unlock(&nlm_file_mutex); } +/* + * Helpers function for resource traversal + * + * nlmsvc_mark_host: + * used by the garbage collector; simply sets h_inuse. + * Always returns 0. + * + * nlmsvc_same_host: + * returns 1 iff the two hosts match. Used to release + * all resources bound to a specific host. + * + * nlmsvc_is_client: + * returns 1 iff the host is a client. + * Used by nlmsvc_invalidate_all + */ +static int +nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy) +{ + host->h_inuse = 1; + return 0; +} + +static int +nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other) +{ + return host == other; +} + +static int +nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy) +{ + return host->h_server; +} + /* * Mark all hosts that still hold resources */ @@ -301,8 +334,7 @@ void nlmsvc_mark_resources(void) { dprintk("lockd: nlmsvc_mark_resources\n"); - - nlm_traverse_files(NULL, NLM_ACT_MARK); + nlm_traverse_files(NULL, nlmsvc_mark_host); } /* @@ -313,23 +345,25 @@ nlmsvc_free_host_resources(struct nlm_host *host) { dprintk("lockd: nlmsvc_free_host_resources\n"); - if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) + if (nlm_traverse_files(host, nlmsvc_same_host)) { printk(KERN_WARNING - "lockd: couldn't remove all locks held by %s", + "lockd: couldn't remove all locks held by %s\n", host->h_name); + BUG(); + } } /* - * delete all hosts structs for clients + * Remove all locks held for clients */ void nlmsvc_invalidate_all(void) { - struct nlm_host *host; - while ((host = nlm_find_client()) != NULL) { - nlmsvc_free_host_resources(host); - host->h_expires = 0; - host->h_killed = 1; - nlm_release_host(host); - } + /* Release all locks held by NFS clients. + * Previously, the code would call + * nlmsvc_free_host_resources for each client in + * turn, which is about as inefficient as it gets. + * Now we just do it once in nlm_traverse_files. + */ + nlm_traverse_files(NULL, nlmsvc_is_client); } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index cfe141e5d7..e13fa23bd1 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -319,12 +319,25 @@ svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) static struct cache_head *export_table[EXPORT_HASHMAX]; +static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc) +{ + int i; + + for (i = 0; i < fsloc->locations_count; i++) { + kfree(fsloc->locations[i].path); + kfree(fsloc->locations[i].hosts); + } + kfree(fsloc->locations); +} + static void svc_export_put(struct kref *ref) { struct svc_export *exp = container_of(ref, struct svc_export, h.ref); dput(exp->ex_dentry); mntput(exp->ex_mnt); auth_domain_put(exp->ex_client); + kfree(exp->ex_path); + nfsd4_fslocs_free(&exp->ex_fslocs); kfree(exp); } @@ -386,6 +399,69 @@ static int check_export(struct inode *inode, int flags) } +#ifdef CONFIG_NFSD_V4 + +static int +fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) +{ + int len; + int migrated, i, err; + + len = qword_get(mesg, buf, PAGE_SIZE); + if (len != 5 || memcmp(buf, "fsloc", 5)) + return 0; + + /* listsize */ + err = get_int(mesg, &fsloc->locations_count); + if (err) + return err; + if (fsloc->locations_count > MAX_FS_LOCATIONS) + return -EINVAL; + if (fsloc->locations_count == 0) + return 0; + + fsloc->locations = kzalloc(fsloc->locations_count + * sizeof(struct nfsd4_fs_location), GFP_KERNEL); + if (!fsloc->locations) + return -ENOMEM; + for (i=0; i < fsloc->locations_count; i++) { + /* colon separated host list */ + err = -EINVAL; + len = qword_get(mesg, buf, PAGE_SIZE); + if (len <= 0) + goto out_free_all; + err = -ENOMEM; + fsloc->locations[i].hosts = kstrdup(buf, GFP_KERNEL); + if (!fsloc->locations[i].hosts) + goto out_free_all; + err = -EINVAL; + /* slash separated path component list */ + len = qword_get(mesg, buf, PAGE_SIZE); + if (len <= 0) + goto out_free_all; + err = -ENOMEM; + fsloc->locations[i].path = kstrdup(buf, GFP_KERNEL); + if (!fsloc->locations[i].path) + goto out_free_all; + } + /* migrated */ + err = get_int(mesg, &migrated); + if (err) + goto out_free_all; + err = -EINVAL; + if (migrated < 0 || migrated > 1) + goto out_free_all; + fsloc->migrated = migrated; + return 0; +out_free_all: + nfsd4_fslocs_free(fsloc); + return err; +} + +#else /* CONFIG_NFSD_V4 */ +static inline int fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) { return 0; } +#endif + static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) { /* client path expiry [flags anonuid anongid fsid] */ @@ -398,6 +474,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) int an_int; nd.dentry = NULL; + exp.ex_path = NULL; if (mesg[mlen-1] != '\n') return -EINVAL; @@ -428,6 +505,10 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) exp.ex_client = dom; exp.ex_mnt = nd.mnt; exp.ex_dentry = nd.dentry; + exp.ex_path = kstrdup(buf, GFP_KERNEL); + err = -ENOMEM; + if (!exp.ex_path) + goto out; /* expiry */ err = -EINVAL; @@ -435,6 +516,11 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) if (exp.h.expiry_time == 0) goto out; + /* fs locations */ + exp.ex_fslocs.locations = NULL; + exp.ex_fslocs.locations_count = 0; + exp.ex_fslocs.migrated = 0; + /* flags */ err = get_int(&mesg, &an_int); if (err == -ENOENT) @@ -460,6 +546,10 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) err = check_export(nd.dentry->d_inode, exp.ex_flags); if (err) goto out; + + err = fsloc_parse(&mesg, buf, &exp.ex_fslocs); + if (err) + goto out; } expp = svc_export_lookup(&exp); @@ -473,6 +563,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) else exp_put(expp); out: + kfree(exp.ex_path); if (nd.dentry) path_release(&nd); out_no_path: @@ -482,7 +573,8 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) return err; } -static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong); +static void exp_flags(struct seq_file *m, int flag, int fsid, + uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); static int svc_export_show(struct seq_file *m, struct cache_detail *cd, @@ -501,8 +593,8 @@ static int svc_export_show(struct seq_file *m, seq_putc(m, '('); if (test_bit(CACHE_VALID, &h->flags) && !test_bit(CACHE_NEGATIVE, &h->flags)) - exp_flags(m, exp->ex_flags, exp->ex_fsid, - exp->ex_anon_uid, exp->ex_anon_gid); + exp_flags(m, exp->ex_flags, exp->ex_fsid, + exp->ex_anon_uid, exp->ex_anon_gid, &exp->ex_fslocs); seq_puts(m, ")\n"); return 0; } @@ -524,6 +616,10 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) new->ex_client = item->ex_client; new->ex_dentry = dget(item->ex_dentry); new->ex_mnt = mntget(item->ex_mnt); + new->ex_path = NULL; + new->ex_fslocs.locations = NULL; + new->ex_fslocs.locations_count = 0; + new->ex_fslocs.migrated = 0; } static void export_update(struct cache_head *cnew, struct cache_head *citem) @@ -535,6 +631,14 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) new->ex_anon_uid = item->ex_anon_uid; new->ex_anon_gid = item->ex_anon_gid; new->ex_fsid = item->ex_fsid; + new->ex_path = item->ex_path; + item->ex_path = NULL; + new->ex_fslocs.locations = item->ex_fslocs.locations; + item->ex_fslocs.locations = NULL; + new->ex_fslocs.locations_count = item->ex_fslocs.locations_count; + item->ex_fslocs.locations_count = 0; + new->ex_fslocs.migrated = item->ex_fslocs.migrated; + item->ex_fslocs.migrated = 0; } static struct cache_head *svc_export_alloc(void) @@ -1048,30 +1152,21 @@ int exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, struct cache_req *creq) { - struct svc_expkey *fsid_key; struct svc_export *exp; int rv; u32 fsidv[2]; mk_fsid_v1(fsidv, 0); - fsid_key = exp_find_key(clp, 1, fsidv, creq); - if (IS_ERR(fsid_key) && PTR_ERR(fsid_key) == -EAGAIN) + exp = exp_find(clp, 1, fsidv, creq); + if (IS_ERR(exp) && PTR_ERR(exp) == -EAGAIN) return nfserr_dropit; - if (!fsid_key || IS_ERR(fsid_key)) - return nfserr_perm; - - exp = exp_get_by_name(clp, fsid_key->ek_mnt, fsid_key->ek_dentry, creq); if (exp == NULL) - rv = nfserr_perm; + return nfserr_perm; else if (IS_ERR(exp)) - rv = nfserrno(PTR_ERR(exp)); - else { - rv = fh_compose(fhp, exp, - fsid_key->ek_dentry, NULL); - exp_put(exp); - } - cache_put(&fsid_key->h, &svc_expkey_cache); + return nfserrno(PTR_ERR(exp)); + rv = fh_compose(fhp, exp, exp->ex_dentry, NULL); + exp_put(exp); return rv; } @@ -1158,7 +1253,8 @@ static struct flags { { 0, {"", ""}} }; -static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong) +static void exp_flags(struct seq_file *m, int flag, int fsid, + uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) { int first = 0; struct flags *flg; @@ -1174,6 +1270,21 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t seq_printf(m, "%sanonuid=%d", first++?",":"", anonu); if (anong != (gid_t)-2 && anong != (0x10000-2)) seq_printf(m, "%sanongid=%d", first++?",":"", anong); + if (fsloc && fsloc->locations_count > 0) { + char *loctype = (fsloc->migrated) ? "refer" : "replicas"; + int i; + + seq_printf(m, "%s%s=", first++?",":"", loctype); + seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\"); + seq_putc(m, '@'); + seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\"); + for (i = 1; i < fsloc->locations_count; i++) { + seq_putc(m, ';'); + seq_escape(m, fsloc->locations[i].path, ",;@ \t\n\\"); + seq_putc(m, '@'); + seq_escape(m, fsloc->locations[i].hosts, ",;@ \t\n\\"); + } + } } static int e_show(struct seq_file *m, void *p) diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index fe56b38364..9187755661 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -241,7 +241,7 @@ static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = w; while (w > 0) { - if (!svc_take_res_page(rqstp)) + if (!rqstp->rq_respages[rqstp->rq_resused++]) return 0; w -= PAGE_SIZE; } @@ -333,4 +333,5 @@ struct svc_version nfsd_acl_version2 = { .vs_proc = nfsd_acl_procedures2, .vs_dispatch = nfsd_dispatch, .vs_xdrsize = NFS3_SVC_XDRSIZE, + .vs_hidden = 1, }; diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 16e10c170a..d4bdc00c11 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -185,7 +185,7 @@ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = w; while (w > 0) { - if (!svc_take_res_page(rqstp)) + if (!rqstp->rq_respages[rqstp->rq_resused++]) return 0; w -= PAGE_SIZE; } @@ -263,5 +263,6 @@ struct svc_version nfsd_acl_version3 = { .vs_proc = nfsd_acl_procedures3, .vs_dispatch = nfsd_dispatch, .vs_xdrsize = NFS3_SVC_XDRSIZE, + .vs_hidden = 1, }; diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index f61142afea..a5ebc7dbb3 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -160,6 +160,7 @@ nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, struct nfsd3_readres *resp) { int nfserr; + u32 max_blocksize = svc_max_payload(rqstp); dprintk("nfsd: READ(3) %s %lu bytes at %lu\n", SVCFH_fmt(&argp->fh), @@ -172,15 +173,15 @@ nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, */ resp->count = argp->count; - if (NFSSVC_MAXBLKSIZE < resp->count) - resp->count = NFSSVC_MAXBLKSIZE; + if (max_blocksize < resp->count) + resp->count = max_blocksize; svc_reserve(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4); fh_copy(&resp->fh, &argp->fh); nfserr = nfsd_read(rqstp, &resp->fh, NULL, argp->offset, - argp->vec, argp->vlen, + rqstp->rq_vec, argp->vlen, &resp->count); if (nfserr == 0) { struct inode *inode = resp->fh.fh_dentry->d_inode; @@ -210,7 +211,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp, resp->committed = argp->stable; nfserr = nfsd_write(rqstp, &resp->fh, NULL, argp->offset, - argp->vec, argp->vlen, + rqstp->rq_vec, argp->vlen, argp->len, &resp->committed); resp->count = argp->count; @@ -538,15 +539,16 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, struct nfsd3_fsinfores *resp) { int nfserr; + u32 max_blocksize = svc_max_payload(rqstp); dprintk("nfsd: FSINFO(3) %s\n", SVCFH_fmt(&argp->fh)); - resp->f_rtmax = NFSSVC_MAXBLKSIZE; - resp->f_rtpref = NFSSVC_MAXBLKSIZE; + resp->f_rtmax = max_blocksize; + resp->f_rtpref = max_blocksize; resp->f_rtmult = PAGE_SIZE; - resp->f_wtmax = NFSSVC_MAXBLKSIZE; - resp->f_wtpref = NFSSVC_MAXBLKSIZE; + resp->f_wtmax = max_blocksize; + resp->f_wtpref = max_blocksize; resp->f_wtmult = PAGE_SIZE; resp->f_dtpref = PAGE_SIZE; resp->f_maxfilesize = ~(u32) 0; diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 243d94b965..247d518248 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -330,6 +330,7 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p, { unsigned int len; int v,pn; + u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh)) || !(p = xdr_decode_hyper(p, &args->offset))) @@ -337,17 +338,16 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p, len = args->count = ntohl(*p++); - if (len > NFSSVC_MAXBLKSIZE) - len = NFSSVC_MAXBLKSIZE; + if (len > max_blocksize) + len = max_blocksize; /* set up the kvec */ v=0; while (len > 0) { - pn = rqstp->rq_resused; - svc_take_page(rqstp); - args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]); - args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; - len -= args->vec[v].iov_len; + pn = rqstp->rq_resused++; + rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); + rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; + len -= rqstp->rq_vec[v].iov_len; v++; } args->vlen = v; @@ -359,6 +359,7 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, struct nfsd3_writeargs *args) { unsigned int len, v, hdr; + u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh)) || !(p = xdr_decode_hyper(p, &args->offset))) @@ -373,22 +374,22 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, rqstp->rq_arg.len - hdr < len) return 0; - args->vec[0].iov_base = (void*)p; - args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; + rqstp->rq_vec[0].iov_base = (void*)p; + rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; - if (len > NFSSVC_MAXBLKSIZE) - len = NFSSVC_MAXBLKSIZE; + if (len > max_blocksize) + len = max_blocksize; v= 0; - while (len > args->vec[v].iov_len) { - len -= args->vec[v].iov_len; + while (len > rqstp->rq_vec[v].iov_len) { + len -= rqstp->rq_vec[v].iov_len; v++; - args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]); - args->vec[v].iov_len = PAGE_SIZE; + rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]); + rqstp->rq_vec[v].iov_len = PAGE_SIZE; } - args->vec[v].iov_len = len; + rqstp->rq_vec[v].iov_len = len; args->vlen = v+1; - return args->count == args->len && args->vec[0].iov_len > 0; + return args->count == args->len && rqstp->rq_vec[0].iov_len > 0; } int @@ -446,11 +447,11 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p, * This page appears in the rq_res.pages list, but as pages_len is always * 0, it won't get in the way */ - svc_take_page(rqstp); len = ntohl(*p++); if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) return 0; - args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); + args->tname = new = + page_address(rqstp->rq_respages[rqstp->rq_resused++]); args->tlen = len; /* first copy and check from the first page */ old = (char*)p; @@ -522,8 +523,8 @@ nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p, { if (!(p = decode_fh(p, &args->fh))) return 0; - svc_take_page(rqstp); - args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); + args->buffer = + page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -554,8 +555,8 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, if (args->count > PAGE_SIZE) args->count = PAGE_SIZE; - svc_take_page(rqstp); - args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); + args->buffer = + page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -565,6 +566,7 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, struct nfsd3_readdirargs *args) { int len, pn; + u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh))) return 0; @@ -573,13 +575,12 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, args->dircount = ntohl(*p++); args->count = ntohl(*p++); - len = (args->count > NFSSVC_MAXBLKSIZE) ? NFSSVC_MAXBLKSIZE : + len = (args->count > max_blocksize) ? max_blocksize : args->count; args->count = len; while (len > 0) { - pn = rqstp->rq_resused; - svc_take_page(rqstp); + pn = rqstp->rq_resused++; if (!args->buffer) args->buffer = page_address(rqstp->rq_respages[pn]); len -= PAGE_SIZE; @@ -668,7 +669,6 @@ nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->len; if (resp->len & 3) { /* need to pad the tail */ - rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); @@ -693,7 +693,6 @@ nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->count; if (resp->count & 3) { /* need to pad the tail */ - rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3); @@ -768,7 +767,6 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = (resp->count) << 2; /* add the 'tail' to the end of the 'head' page - page 0. */ - rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p++ = 0; /* no more entries */ *p++ = htonl(resp->common.err == nfserr_eof); diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index edb107e61b..5d94555cdc 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -63,6 +63,8 @@ #define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \ | NFS4_ACE_DIRECTORY_INHERIT_ACE | NFS4_ACE_INHERIT_ONLY_ACE) +#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS | NFS4_ACE_IDENTIFIER_GROUP) + #define MASK_EQUAL(mask1, mask2) \ ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) ) @@ -96,24 +98,26 @@ deny_mask(u32 allow_mask, unsigned int flags) /* XXX: modify functions to return NFS errors; they're only ever * used by nfs code, after all.... */ -static int -mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) +/* We only map from NFSv4 to POSIX ACLs when setting ACLs, when we err on the + * side of being more restrictive, so the mode bit mapping below is + * pessimistic. An optimistic version would be needed to handle DENY's, + * but we espect to coalesce all ALLOWs and DENYs before mapping to mode + * bits. */ + +static void +low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) { - u32 ignore = 0; + u32 write_mode = NFS4_WRITE_MODE; - if (!(flags & NFS4_ACL_DIR)) - ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */ - perm |= ignore; + if (flags & NFS4_ACL_DIR) + write_mode |= NFS4_ACE_DELETE_CHILD; *mode = 0; if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE) *mode |= ACL_READ; - if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE) + if ((perm & write_mode) == write_mode) *mode |= ACL_WRITE; if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE) *mode |= ACL_EXECUTE; - if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags))) - return -EINVAL; - return 0; } struct ace_container { @@ -338,38 +342,6 @@ sort_pacl(struct posix_acl *pacl) return; } -static int -write_pace(struct nfs4_ace *ace, struct posix_acl *pacl, - struct posix_acl_entry **pace, short tag, unsigned int flags) -{ - struct posix_acl_entry *this = *pace; - - if (*pace == pacl->a_entries + pacl->a_count) - return -EINVAL; /* fell off the end */ - (*pace)++; - this->e_tag = tag; - if (tag == ACL_USER_OBJ) - flags |= NFS4_ACL_OWNER; - if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags)) - return -EINVAL; - this->e_id = (tag == ACL_USER || tag == ACL_GROUP ? - ace->who : ACL_UNDEFINED_ID); - return 0; -} - -static struct nfs4_ace * -get_next_v4_ace(struct list_head **p, struct list_head *head) -{ - struct nfs4_ace *ace; - - *p = (*p)->next; - if (*p == head) - return NULL; - ace = list_entry(*p, struct nfs4_ace, l_ace); - - return ace; -} - int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, struct posix_acl **dpacl, unsigned int flags) @@ -385,42 +357,23 @@ nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, goto out; error = nfs4_acl_split(acl, dacl); - if (error < 0) + if (error) goto out_acl; - if (pacl != NULL) { - if (acl->naces == 0) { - error = -ENODATA; - goto try_dpacl; - } - - *pacl = _nfsv4_to_posix_one(acl, flags); - if (IS_ERR(*pacl)) { - error = PTR_ERR(*pacl); - *pacl = NULL; - goto out_acl; - } + *pacl = _nfsv4_to_posix_one(acl, flags); + if (IS_ERR(*pacl)) { + error = PTR_ERR(*pacl); + *pacl = NULL; + goto out_acl; } -try_dpacl: - if (dpacl != NULL) { - if (dacl->naces == 0) { - if (pacl == NULL || *pacl == NULL) - error = -ENODATA; - goto out_acl; - } - - error = 0; - *dpacl = _nfsv4_to_posix_one(dacl, flags); - if (IS_ERR(*dpacl)) { - error = PTR_ERR(*dpacl); - *dpacl = NULL; - goto out_acl; - } + *dpacl = _nfsv4_to_posix_one(dacl, flags); + if (IS_ERR(*dpacl)) { + error = PTR_ERR(*dpacl); + *dpacl = NULL; } - out_acl: - if (error && pacl) { + if (error) { posix_acl_release(*pacl); *pacl = NULL; } @@ -429,349 +382,311 @@ out: return error; } +/* + * While processing the NFSv4 ACE, this maintains bitmasks representing + * which permission bits have been allowed and which denied to a given + * entity: */ +struct posix_ace_state { + u32 allow; + u32 deny; +}; + +struct posix_user_ace_state { + uid_t uid; + struct posix_ace_state perms; +}; + +struct posix_ace_state_array { + int n; + struct posix_user_ace_state aces[]; +}; + +/* + * While processing the NFSv4 ACE, this maintains the partial permissions + * calculated so far: */ + +struct posix_acl_state { + struct posix_ace_state owner; + struct posix_ace_state group; + struct posix_ace_state other; + struct posix_ace_state everyone; + struct posix_ace_state mask; /* Deny unused in this case */ + struct posix_ace_state_array *users; + struct posix_ace_state_array *groups; +}; + static int -same_who(struct nfs4_ace *a, struct nfs4_ace *b) +init_state(struct posix_acl_state *state, int cnt) { - return a->whotype == b->whotype && - (a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who); + int alloc; + + memset(state, 0, sizeof(struct posix_acl_state)); + /* + * In the worst case, each individual acl could be for a distinct + * named user or group, but we don't no which, so we allocate + * enough space for either: + */ + alloc = sizeof(struct posix_ace_state_array) + + cnt*sizeof(struct posix_ace_state); + state->users = kzalloc(alloc, GFP_KERNEL); + if (!state->users) + return -ENOMEM; + state->groups = kzalloc(alloc, GFP_KERNEL); + if (!state->groups) { + kfree(state->users); + return -ENOMEM; + } + return 0; } -static int -complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny, - unsigned int flags) -{ - int ignore = 0; - if (!(flags & NFS4_ACL_DIR)) - ignore |= NFS4_ACE_DELETE_CHILD; - return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags), - ignore|deny->access_mask) && - allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE && - deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE && - allow->flag == deny->flag && - same_who(allow, deny); +static void +free_state(struct posix_acl_state *state) { + kfree(state->users); + kfree(state->groups); } -static inline int -user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p, - struct posix_acl *pacl, struct posix_acl_entry **pace, - unsigned int flags) +static inline void add_to_mask(struct posix_acl_state *state, struct posix_ace_state *astate) { - int error = -EINVAL; - struct nfs4_ace *ace, *ace2; - - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - if (ace2type(ace) != ACL_USER_OBJ) - goto out; - error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags); - if (error < 0) - goto out; - error = -EINVAL; - ace2 = get_next_v4_ace(p, &n4acl->ace_head); - if (ace2 == NULL) - goto out; - if (!complementary_ace_pair(ace, ace2, flags)) - goto out; - error = 0; -out: - return error; + state->mask.allow |= astate->allow; } -static inline int -users_from_v4(struct nfs4_acl *n4acl, struct list_head **p, - struct nfs4_ace **mask_ace, - struct posix_acl *pacl, struct posix_acl_entry **pace, - unsigned int flags) -{ - int error = -EINVAL; - struct nfs4_ace *ace, *ace2; +/* + * Certain bits (SYNCHRONIZE, DELETE, WRITE_OWNER, READ/WRITE_NAMED_ATTRS, + * READ_ATTRIBUTES, READ_ACL) are currently unenforceable and don't translate + * to traditional read/write/execute permissions. + * + * It's problematic to reject acls that use certain mode bits, because it + * places the burden on users to learn the rules about which bits one + * particular server sets, without giving the user a lot of help--we return an + * error that could mean any number of different things. To make matters + * worse, the problematic bits might be introduced by some application that's + * automatically mapping from some other acl model. + * + * So wherever possible we accept anything, possibly erring on the side of + * denying more permissions than necessary. + * + * However we do reject *explicit* DENY's of a few bits representing + * permissions we could never deny: + */ - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - while (ace2type(ace) == ACL_USER) { - if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) - goto out; - if (*mask_ace && - !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) - goto out; - *mask_ace = ace; - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) - goto out; - error = write_pace(ace, pacl, pace, ACL_USER, flags); - if (error < 0) - goto out; - error = -EINVAL; - ace2 = get_next_v4_ace(p, &n4acl->ace_head); - if (ace2 == NULL) - goto out; - if (!complementary_ace_pair(ace, ace2, flags)) - goto out; - if ((*mask_ace)->flag != ace2->flag || - !same_who(*mask_ace, ace2)) - goto out; - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - } - error = 0; -out: - return error; +static inline int check_deny(u32 mask, int isowner) +{ + if (mask & (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL)) + return -EINVAL; + if (!isowner) + return 0; + if (mask & (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)) + return -EINVAL; + return 0; } -static inline int -group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p, - struct nfs4_ace **mask_ace, - struct posix_acl *pacl, struct posix_acl_entry **pace, - unsigned int flags) +static struct posix_acl * +posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) { - int error = -EINVAL; - struct nfs4_ace *ace, *ace2; - struct ace_container *ac; - struct list_head group_l; - - INIT_LIST_HEAD(&group_l); - ace = list_entry(*p, struct nfs4_ace, l_ace); - - /* group owner (mask and allow aces) */ + struct posix_acl_entry *pace; + struct posix_acl *pacl; + int nace; + int i, error = 0; - if (pacl->a_count != 3) { - /* then the group owner should be preceded by mask */ - if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) - goto out; - if (*mask_ace && - !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) - goto out; - *mask_ace = ace; - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; + nace = 4 + state->users->n + state->groups->n; + pacl = posix_acl_alloc(nace, GFP_KERNEL); + if (!pacl) + return ERR_PTR(-ENOMEM); - if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace)) - goto out; + pace = pacl->a_entries; + pace->e_tag = ACL_USER_OBJ; + error = check_deny(state->owner.deny, 1); + if (error) + goto out_err; + low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags); + pace->e_id = ACL_UNDEFINED_ID; + + for (i=0; i < state->users->n; i++) { + pace++; + pace->e_tag = ACL_USER; + error = check_deny(state->users->aces[i].perms.deny, 0); + if (error) + goto out_err; + low_mode_from_nfs4(state->users->aces[i].perms.allow, + &pace->e_perm, flags); + pace->e_id = state->users->aces[i].uid; + add_to_mask(state, &state->users->aces[i].perms); } - if (ace2type(ace) != ACL_GROUP_OBJ) - goto out; - - ac = kmalloc(sizeof(*ac), GFP_KERNEL); - error = -ENOMEM; - if (ac == NULL) - goto out; - ac->ace = ace; - list_add_tail(&ac->ace_l, &group_l); - - error = -EINVAL; - if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) - goto out; - - error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags); - if (error < 0) - goto out; - - error = -EINVAL; - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - - /* groups (mask and allow aces) */ - - while (ace2type(ace) == ACL_GROUP) { - if (*mask_ace == NULL) - goto out; - - if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE || - !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) - goto out; - *mask_ace = ace; + pace++; + pace->e_tag = ACL_GROUP_OBJ; + error = check_deny(state->group.deny, 0); + if (error) + goto out_err; + low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags); + pace->e_id = ACL_UNDEFINED_ID; + add_to_mask(state, &state->group); + + for (i=0; i < state->groups->n; i++) { + pace++; + pace->e_tag = ACL_GROUP; + error = check_deny(state->groups->aces[i].perms.deny, 0); + if (error) + goto out_err; + low_mode_from_nfs4(state->groups->aces[i].perms.allow, + &pace->e_perm, flags); + pace->e_id = state->groups->aces[i].uid; + add_to_mask(state, &state->groups->aces[i].perms); + } - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - ac = kmalloc(sizeof(*ac), GFP_KERNEL); - error = -ENOMEM; - if (ac == NULL) - goto out; - error = -EINVAL; - if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE || - !same_who(ace, *mask_ace)) - goto out; + pace++; + pace->e_tag = ACL_MASK; + low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags); + pace->e_id = ACL_UNDEFINED_ID; - ac->ace = ace; - list_add_tail(&ac->ace_l, &group_l); + pace++; + pace->e_tag = ACL_OTHER; + error = check_deny(state->other.deny, 0); + if (error) + goto out_err; + low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags); + pace->e_id = ACL_UNDEFINED_ID; - error = write_pace(ace, pacl, pace, ACL_GROUP, flags); - if (error < 0) - goto out; - error = -EINVAL; - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - } + return pacl; +out_err: + posix_acl_release(pacl); + return ERR_PTR(error); +} - /* group owner (deny ace) */ +static inline void allow_bits(struct posix_ace_state *astate, u32 mask) +{ + /* Allow all bits in the mask not already denied: */ + astate->allow |= mask & ~astate->deny; +} - if (ace2type(ace) != ACL_GROUP_OBJ) - goto out; - ac = list_entry(group_l.next, struct ace_container, ace_l); - ace2 = ac->ace; - if (!complementary_ace_pair(ace2, ace, flags)) - goto out; - list_del(group_l.next); - kfree(ac); +static inline void deny_bits(struct posix_ace_state *astate, u32 mask) +{ + /* Deny all bits in the mask not already allowed: */ + astate->deny |= mask & ~astate->allow; +} - /* groups (deny aces) */ +static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array *a, uid_t uid) +{ + int i; - while (!list_empty(&group_l)) { - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - if (ace2type(ace) != ACL_GROUP) - goto out; - ac = list_entry(group_l.next, struct ace_container, ace_l); - ace2 = ac->ace; - if (!complementary_ace_pair(ace2, ace, flags)) - goto out; - list_del(group_l.next); - kfree(ac); - } + for (i = 0; i < a->n; i++) + if (a->aces[i].uid == uid) + return i; + /* Not found: */ + a->n++; + a->aces[i].uid = uid; + a->aces[i].perms.allow = state->everyone.allow; + a->aces[i].perms.deny = state->everyone.deny; - ace = get_next_v4_ace(p, &n4acl->ace_head); - if (ace == NULL) - goto out; - if (ace2type(ace) != ACL_OTHER) - goto out; - error = 0; -out: - while (!list_empty(&group_l)) { - ac = list_entry(group_l.next, struct ace_container, ace_l); - list_del(group_l.next); - kfree(ac); - } - return error; + return i; } -static inline int -mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p, - struct nfs4_ace **mask_ace, - struct posix_acl *pacl, struct posix_acl_entry **pace, - unsigned int flags) +static void deny_bits_array(struct posix_ace_state_array *a, u32 mask) { - int error = -EINVAL; - struct nfs4_ace *ace; + int i; - ace = list_entry(*p, struct nfs4_ace, l_ace); - if (pacl->a_count != 3) { - if (*mask_ace == NULL) - goto out; - (*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags); - write_pace(*mask_ace, pacl, pace, ACL_MASK, flags); - } - error = 0; -out: - return error; + for (i=0; i < a->n; i++) + deny_bits(&a->aces[i].perms, mask); } -static inline int -other_from_v4(struct nfs4_acl *n4acl, struct list_head **p, - struct posix_acl *pacl, struct posix_acl_entry **pace, - unsigned int flags) +static void allow_bits_array(struct posix_ace_state_array *a, u32 mask) { - int error = -EINVAL; - struct nfs4_ace *ace, *ace2; + int i; - ace = list_entry(*p, struct nfs4_ace, l_ace); - if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) - goto out; - error = write_pace(ace, pacl, pace, ACL_OTHER, flags); - if (error < 0) - goto out; - error = -EINVAL; - ace2 = get_next_v4_ace(p, &n4acl->ace_head); - if (ace2 == NULL) - goto out; - if (!complementary_ace_pair(ace, ace2, flags)) - goto out; - error = 0; -out: - return error; + for (i=0; i < a->n; i++) + allow_bits(&a->aces[i].perms, mask); } -static int -calculate_posix_ace_count(struct nfs4_acl *n4acl) +static void process_one_v4_ace(struct posix_acl_state *state, + struct nfs4_ace *ace) { - if (n4acl->naces == 6) /* owner, owner group, and other only */ - return 3; - else { /* Otherwise there must be a mask entry. */ - /* Also, the remaining entries are for named users and - * groups, and come in threes (mask, allow, deny): */ - if (n4acl->naces < 7) - return -EINVAL; - if ((n4acl->naces - 7) % 3) - return -EINVAL; - return 4 + (n4acl->naces - 7)/3; + u32 mask = ace->access_mask; + int i; + + switch (ace2type(ace)) { + case ACL_USER_OBJ: + if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { + allow_bits(&state->owner, mask); + } else { + deny_bits(&state->owner, mask); + } + break; + case ACL_USER: + i = find_uid(state, state->users, ace->who); + if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { + allow_bits(&state->users->aces[i].perms, mask); + } else { + deny_bits(&state->users->aces[i].perms, mask); + mask = state->users->aces[i].perms.deny; + deny_bits(&state->owner, mask); + } + break; + case ACL_GROUP_OBJ: + if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { + allow_bits(&state->group, mask); + } else { + deny_bits(&state->group, mask); + mask = state->group.deny; + deny_bits(&state->owner, mask); + deny_bits(&state->everyone, mask); + deny_bits_array(state->users, mask); + deny_bits_array(state->groups, mask); + } + break; + case ACL_GROUP: + i = find_uid(state, state->groups, ace->who); + if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { + allow_bits(&state->groups->aces[i].perms, mask); + } else { + deny_bits(&state->groups->aces[i].perms, mask); + mask = state->groups->aces[i].perms.deny; + deny_bits(&state->owner, mask); + deny_bits(&state->group, mask); + deny_bits(&state->everyone, mask); + deny_bits_array(state->users, mask); + deny_bits_array(state->groups, mask); + } + break; + case ACL_OTHER: + if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { + allow_bits(&state->owner, mask); + allow_bits(&state->group, mask); + allow_bits(&state->other, mask); + allow_bits(&state->everyone, mask); + allow_bits_array(state->users, mask); + allow_bits_array(state->groups, mask); + } else { + deny_bits(&state->owner, mask); + deny_bits(&state->group, mask); + deny_bits(&state->other, mask); + deny_bits(&state->everyone, mask); + deny_bits_array(state->users, mask); + deny_bits_array(state->groups, mask); + } } } - static struct posix_acl * _nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags) { + struct posix_acl_state state; struct posix_acl *pacl; - int error = -EINVAL, nace = 0; - struct list_head *p; - struct nfs4_ace *mask_ace = NULL; - struct posix_acl_entry *pace; - - nace = calculate_posix_ace_count(n4acl); - if (nace < 0) - goto out_err; - - pacl = posix_acl_alloc(nace, GFP_KERNEL); - error = -ENOMEM; - if (pacl == NULL) - goto out_err; - - pace = &pacl->a_entries[0]; - p = &n4acl->ace_head; - - error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags); - if (error) - goto out_acl; - - error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); - if (error) - goto out_acl; + struct nfs4_ace *ace; + int ret; - error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace, - flags); - if (error) - goto out_acl; + ret = init_state(&state, n4acl->naces); + if (ret) + return ERR_PTR(ret); - error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); - if (error) - goto out_acl; - error = other_from_v4(n4acl, &p, pacl, &pace, flags); - if (error) - goto out_acl; + list_for_each_entry(ace, &n4acl->ace_head, l_ace) + process_one_v4_ace(&state, ace); - error = -EINVAL; - if (p->next != &n4acl->ace_head) - goto out_acl; - if (pace != pacl->a_entries + pacl->a_count) - goto out_acl; + pacl = posix_state_to_acl(&state, flags); - sort_pacl(pacl); + free_state(&state); - return pacl; -out_acl: - posix_acl_release(pacl); -out_err: - pacl = ERR_PTR(error); + if (!IS_ERR(pacl)) + sort_pacl(pacl); return pacl; } @@ -785,22 +700,41 @@ nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl) list_for_each_safe(h, n, &acl->ace_head) { ace = list_entry(h, struct nfs4_ace, l_ace); - if ((ace->flag & NFS4_INHERITANCE_FLAGS) - != NFS4_INHERITANCE_FLAGS) - continue; + if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE && + ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) + return -EINVAL; - error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, - ace->access_mask, ace->whotype, ace->who); - if (error < 0) - goto out; + if (ace->flag & ~NFS4_SUPPORTED_FLAGS) + return -EINVAL; - list_del(h); - kfree(ace); - acl->naces--; + switch (ace->flag & NFS4_INHERITANCE_FLAGS) { + case 0: + /* Leave this ace in the effective acl: */ + continue; + case NFS4_INHERITANCE_FLAGS: + /* Add this ace to the default acl and remove it + * from the effective acl: */ + error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, + ace->access_mask, ace->whotype, ace->who); + if (error) + return error; + list_del(h); + kfree(ace); + acl->naces--; + break; + case NFS4_INHERITANCE_FLAGS & ~NFS4_ACE_INHERIT_ONLY_ACE: + /* Add this ace to the default, but leave it in + * the effective acl as well: */ + error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, + ace->access_mask, ace->whotype, ace->who); + if (error) + return error; + break; + default: + return -EINVAL; + } } - -out: - return error; + return 0; } static short @@ -930,23 +864,6 @@ nfs4_acl_write_who(int who, char *p) return -1; } -static inline int -match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who) -{ - switch (ace->whotype) { - case NFS4_ACL_WHO_NAMED: - return who == ace->who; - case NFS4_ACL_WHO_OWNER: - return who == owner; - case NFS4_ACL_WHO_GROUP: - return who == group; - case NFS4_ACL_WHO_EVERYONE: - return 1; - default: - return 0; - } -} - EXPORT_SYMBOL(nfs4_acl_new); EXPORT_SYMBOL(nfs4_acl_free); EXPORT_SYMBOL(nfs4_acl_add_ace); diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 15ded7a30a..8333db12ca 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -646,7 +646,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ *p++ = nfssvc_boot.tv_usec; status = nfsd_write(rqstp, current_fh, filp, write->wr_offset, - write->wr_vec, write->wr_vlen, write->wr_buflen, + rqstp->rq_vec, write->wr_vlen, write->wr_buflen, &write->wr_how_written); if (filp) fput(filp); @@ -802,13 +802,29 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, * SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH * require a valid current filehandle */ - if ((!current_fh->fh_dentry) && - !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) || - (op->opnum == OP_SETCLIENTID) || - (op->opnum == OP_SETCLIENTID_CONFIRM) || - (op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) || - (op->opnum == OP_RELEASE_LOCKOWNER))) { - op->status = nfserr_nofilehandle; + if (!current_fh->fh_dentry) { + if (!((op->opnum == OP_PUTFH) || + (op->opnum == OP_PUTROOTFH) || + (op->opnum == OP_SETCLIENTID) || + (op->opnum == OP_SETCLIENTID_CONFIRM) || + (op->opnum == OP_RENEW) || + (op->opnum == OP_RESTOREFH) || + (op->opnum == OP_RELEASE_LOCKOWNER))) { + op->status = nfserr_nofilehandle; + goto encode_op; + } + } + /* Check must be done at start of each operation, except + * for GETATTR and ops not listed as returning NFS4ERR_MOVED + */ + else if (current_fh->fh_export->ex_fslocs.migrated && + !((op->opnum == OP_GETATTR) || + (op->opnum == OP_PUTROOTFH) || + (op->opnum == OP_PUTPUBFH) || + (op->opnum == OP_RENEW) || + (op->opnum == OP_SETCLIENTID) || + (op->opnum == OP_RELEASE_LOCKOWNER))) { + op->status = nfserr_moved; goto encode_op; } switch (op->opnum) { diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 5be00436b5..41fc241b72 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -60,6 +60,14 @@ #define NFSDDBG_FACILITY NFSDDBG_XDR +/* + * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing + * directory in order to indicate to the client that a filesystem boundary is present + * We use a fixed fsid for a referral + */ +#define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL +#define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL + static int check_filename(char *str, int len, int err) { @@ -926,26 +934,26 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); goto xdr_error; } - write->wr_vec[0].iov_base = p; - write->wr_vec[0].iov_len = avail; + argp->rqstp->rq_vec[0].iov_base = p; + argp->rqstp->rq_vec[0].iov_len = avail; v = 0; len = write->wr_buflen; - while (len > write->wr_vec[v].iov_len) { - len -= write->wr_vec[v].iov_len; + while (len > argp->rqstp->rq_vec[v].iov_len) { + len -= argp->rqstp->rq_vec[v].iov_len; v++; - write->wr_vec[v].iov_base = page_address(argp->pagelist[0]); + argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]); argp->pagelist++; if (argp->pagelen >= PAGE_SIZE) { - write->wr_vec[v].iov_len = PAGE_SIZE; + argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE; argp->pagelen -= PAGE_SIZE; } else { - write->wr_vec[v].iov_len = argp->pagelen; + argp->rqstp->rq_vec[v].iov_len = argp->pagelen; argp->pagelen -= len; } } - argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len); - argp->p = (u32*) (write->wr_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); - write->wr_vec[v].iov_len = len; + argp->end = (u32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len); + argp->p = (u32*) (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); + argp->rqstp->rq_vec[v].iov_len = len; write->wr_vlen = v+1; DECODE_TAIL; @@ -1223,6 +1231,119 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) stateowner->so_replay.rp_buflen); \ } } while (0); +/* Encode as an array of strings the string given with components + * seperated @sep. + */ +static int nfsd4_encode_components(char sep, char *components, + u32 **pp, int *buflen) +{ + u32 *p = *pp; + u32 *countp = p; + int strlen, count=0; + char *str, *end; + + dprintk("nfsd4_encode_components(%s)\n", components); + if ((*buflen -= 4) < 0) + return nfserr_resource; + WRITE32(0); /* We will fill this in with @count later */ + end = str = components; + while (*end) { + for (; *end && (*end != sep); end++) + ; /* Point to end of component */ + strlen = end - str; + if (strlen) { + if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0) + return nfserr_resource; + WRITE32(strlen); + WRITEMEM(str, strlen); + count++; + } + else + end++; + str = end; + } + *pp = p; + p = countp; + WRITE32(count); + return 0; +} + +/* + * encode a location element of a fs_locations structure + */ +static int nfsd4_encode_fs_location4(struct nfsd4_fs_location *location, + u32 **pp, int *buflen) +{ + int status; + u32 *p = *pp; + + status = nfsd4_encode_components(':', location->hosts, &p, buflen); + if (status) + return status; + status = nfsd4_encode_components('/', location->path, &p, buflen); + if (status) + return status; + *pp = p; + return 0; +} + +/* + * Return the path to an export point in the pseudo filesystem namespace + * Returned string is safe to use as long as the caller holds a reference + * to @exp. + */ +static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp) +{ + struct svc_fh tmp_fh; + char *path, *rootpath; + int stat; + + fh_init(&tmp_fh, NFS4_FHSIZE); + stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle); + if (stat) + return ERR_PTR(stat); + rootpath = tmp_fh.fh_export->ex_path; + + path = exp->ex_path; + + if (strncmp(path, rootpath, strlen(rootpath))) { + printk("nfsd: fs_locations failed;" + "%s is not contained in %s\n", path, rootpath); + return ERR_PTR(-EOPNOTSUPP); + } + + return path + strlen(rootpath); +} + +/* + * encode a fs_locations structure + */ +static int nfsd4_encode_fs_locations(struct svc_rqst *rqstp, + struct svc_export *exp, + u32 **pp, int *buflen) +{ + int status, i; + u32 *p = *pp; + struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; + char *root = nfsd4_path(rqstp, exp); + + if (IS_ERR(root)) + return PTR_ERR(root); + status = nfsd4_encode_components('/', root, &p, buflen); + if (status) + return status; + if ((*buflen -= 4) < 0) + return nfserr_resource; + WRITE32(fslocs->locations_count); + for (i=0; ilocations_count; i++) { + status = nfsd4_encode_fs_location4(&fslocs->locations[i], + &p, buflen); + if (status) + return status; + } + *pp = p; + return 0; +} static u32 nfs4_ftypes[16] = { NF4BAD, NF4FIFO, NF4CHR, NF4BAD, @@ -1272,6 +1393,25 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); } +#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ + FATTR4_WORD0_RDATTR_ERROR) +#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID + +static int fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err) +{ + /* As per referral draft: */ + if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS || + *bmval1 & ~WORD1_ABSENT_FS_ATTRS) { + if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR || + *bmval0 & FATTR4_WORD0_FS_LOCATIONS) + *rdattr_err = NFSERR_MOVED; + else + return nfserr_moved; + } + *bmval0 &= WORD0_ABSENT_FS_ATTRS; + *bmval1 &= WORD1_ABSENT_FS_ATTRS; + return 0; +} /* * Note: @fhp can be NULL; in this case, we might have to compose the filehandle @@ -1294,6 +1434,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, u32 *attrlenp; u32 dummy; u64 dummy64; + u32 rdattr_err = 0; u32 *p = buffer; int status; int aclsupport = 0; @@ -1303,6 +1444,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0); BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1); + if (exp->ex_fslocs.migrated) { + status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err); + if (status) + goto out; + } + status = vfs_getattr(exp->ex_mnt, dentry, &stat); if (status) goto out_nfserr; @@ -1334,6 +1481,11 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, goto out_nfserr; } } + if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { + if (exp->ex_fslocs.locations == NULL) { + bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS; + } + } if ((buflen -= 16) < 0) goto out_resource; @@ -1343,12 +1495,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, attrlenp = p++; /* to be backfilled later */ if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { + u32 word0 = NFSD_SUPPORTED_ATTRS_WORD0; if ((buflen -= 12) < 0) goto out_resource; + if (!aclsupport) + word0 &= ~FATTR4_WORD0_ACL; + if (!exp->ex_fslocs.locations) + word0 &= ~FATTR4_WORD0_FS_LOCATIONS; WRITE32(2); - WRITE32(aclsupport ? - NFSD_SUPPORTED_ATTRS_WORD0 : - NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL); + WRITE32(word0); WRITE32(NFSD_SUPPORTED_ATTRS_WORD1); } if (bmval0 & FATTR4_WORD0_TYPE) { @@ -1402,7 +1557,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_FSID) { if ((buflen -= 16) < 0) goto out_resource; - if (is_fsid(fhp, rqstp->rq_reffh)) { + if (exp->ex_fslocs.migrated) { + WRITE64(NFS4_REFERRAL_FSID_MAJOR); + WRITE64(NFS4_REFERRAL_FSID_MINOR); + } else if (is_fsid(fhp, rqstp->rq_reffh)) { WRITE64((u64)exp->ex_fsid); WRITE64((u64)0); } else { @@ -1425,7 +1583,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { if ((buflen -= 4) < 0) goto out_resource; - WRITE32(0); + WRITE32(rdattr_err); } if (bmval0 & FATTR4_WORD0_ACL) { struct nfs4_ace *ace; @@ -1513,6 +1671,13 @@ out_acl: goto out_resource; WRITE64((u64) statfs.f_files); } + if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { + status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen); + if (status == nfserr_resource) + goto out_resource; + if (status) + goto out; + } if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { if ((buflen -= 4) < 0) goto out_resource; @@ -1536,12 +1701,12 @@ out_acl: if (bmval0 & FATTR4_WORD0_MAXREAD) { if ((buflen -= 8) < 0) goto out_resource; - WRITE64((u64) NFSSVC_MAXBLKSIZE); + WRITE64((u64) svc_max_payload(rqstp)); } if (bmval0 & FATTR4_WORD0_MAXWRITE) { if ((buflen -= 8) < 0) goto out_resource; - WRITE64((u64) NFSSVC_MAXBLKSIZE); + WRITE64((u64) svc_max_payload(rqstp)); } if (bmval1 & FATTR4_WORD1_MODE) { if ((buflen -= 4) < 0) @@ -1845,7 +2010,6 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_ge nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, resp->p, &buflen, getattr->ga_bmval, resp->rqstp); - if (!nfserr) resp->p += buflen; return nfserr; @@ -2039,7 +2203,8 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct n } static int -nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read) +nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, + struct nfsd4_read *read) { u32 eof; int v, pn; @@ -2054,31 +2219,33 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read RESERVE_SPACE(8); /* eof flag and byte count */ - maxcount = NFSSVC_MAXBLKSIZE; + maxcount = svc_max_payload(resp->rqstp); if (maxcount > read->rd_length) maxcount = read->rd_length; len = maxcount; v = 0; while (len > 0) { - pn = resp->rqstp->rq_resused; - svc_take_page(resp->rqstp); - read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]); - read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE; + pn = resp->rqstp->rq_resused++; + resp->rqstp->rq_vec[v].iov_base = + page_address(resp->rqstp->rq_respages[pn]); + resp->rqstp->rq_vec[v].iov_len = + len < PAGE_SIZE ? len : PAGE_SIZE; v++; len -= PAGE_SIZE; } read->rd_vlen = v; nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp, - read->rd_offset, read->rd_iov, read->rd_vlen, + read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, &maxcount); if (nfserr == nfserr_symlink) nfserr = nfserr_inval; if (nfserr) return nfserr; - eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size); + eof = (read->rd_offset + maxcount >= + read->rd_fhp->fh_dentry->d_inode->i_size); WRITE32(eof); WRITE32(maxcount); @@ -2088,7 +2255,6 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read resp->xbuf->page_len = maxcount; /* Use rest of head for padding and remaining ops: */ - resp->rqstp->rq_restailpage = 0; resp->xbuf->tail[0].iov_base = p; resp->xbuf->tail[0].iov_len = 0; if (maxcount&3) { @@ -2113,8 +2279,7 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r if (resp->xbuf->page_len) return nfserr_resource; - svc_take_page(resp->rqstp); - page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); maxcount = PAGE_SIZE; RESERVE_SPACE(4); @@ -2138,7 +2303,6 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r resp->xbuf->page_len = maxcount; /* Use rest of head for padding and remaining ops: */ - resp->rqstp->rq_restailpage = 0; resp->xbuf->tail[0].iov_base = p; resp->xbuf->tail[0].iov_len = 0; if (maxcount&3) { @@ -2189,8 +2353,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re goto err_no_verf; } - svc_take_page(resp->rqstp); - page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); readdir->common.err = 0; readdir->buflen = maxcount; readdir->buffer = page; @@ -2215,10 +2378,10 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re p = readdir->buffer; *p++ = 0; /* no more entries */ *p++ = htonl(readdir->common.err == nfserr_eof); - resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); + resp->xbuf->page_len = ((char*)p) - (char*)page_address( + resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); /* Use rest of head for padding and remaining ops: */ - resp->rqstp->rq_restailpage = 0; resp->xbuf->tail[0].iov_base = tailbase; resp->xbuf->tail[0].iov_len = 0; resp->p = resp->xbuf->tail[0].iov_base; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 5c6a477c20..39aed90151 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -57,6 +57,7 @@ enum { NFSD_Pool_Threads, NFSD_Versions, NFSD_Ports, + NFSD_MaxBlkSize, /* * The below MUST come last. Otherwise we leave a hole in nfsd_files[] * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops @@ -82,6 +83,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size); static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); static ssize_t write_versions(struct file *file, char *buf, size_t size); static ssize_t write_ports(struct file *file, char *buf, size_t size); +static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); #ifdef CONFIG_NFSD_V4 static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); @@ -100,6 +102,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Pool_Threads] = write_pool_threads, [NFSD_Versions] = write_versions, [NFSD_Ports] = write_ports, + [NFSD_MaxBlkSize] = write_maxblksize, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = write_leasetime, [NFSD_RecoveryDir] = write_recoverydir, @@ -523,18 +526,20 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) err = nfsd_create_serv(); if (!err) { int proto = 0; - err = lockd_up(proto); - if (!err) { - err = svc_addsock(nfsd_serv, fd, buf, &proto); - if (err) - lockd_down(); + err = svc_addsock(nfsd_serv, fd, buf, &proto); + if (err >= 0) { + err = lockd_up(proto); + if (err < 0) + svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf); } /* Decrease the count, but don't shutdown the * the service */ + lock_kernel(); nfsd_serv->sv_nrthreads--; + unlock_kernel(); } - return err; + return err < 0 ? err : 0; } if (buf[0] == '-') { char *toclose = kstrdup(buf+1, GFP_KERNEL); @@ -545,12 +550,43 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) if (nfsd_serv) len = svc_sock_names(buf, nfsd_serv, toclose); unlock_kernel(); + if (len >= 0) + lockd_down(); kfree(toclose); return len; } return -EINVAL; } +int nfsd_max_blksize; + +static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) +{ + char *mesg = buf; + if (size > 0) { + int bsize; + int rv = get_int(&mesg, &bsize); + if (rv) + return rv; + /* force bsize into allowed range and + * required alignment. + */ + if (bsize < 1024) + bsize = 1024; + if (bsize > NFSSVC_MAXBLKSIZE) + bsize = NFSSVC_MAXBLKSIZE; + bsize &= ~(1024-1); + lock_kernel(); + if (nfsd_serv && nfsd_serv->sv_nrthreads) { + unlock_kernel(); + return -EBUSY; + } + nfsd_max_blksize = bsize; + unlock_kernel(); + } + return sprintf(buf, "%d\n", nfsd_max_blksize); +} + #ifdef CONFIG_NFSD_V4 extern time_t nfs4_leasetime(void); @@ -616,6 +652,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, + [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 06cd0db0f3..9ee1dab5d4 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -146,20 +146,20 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp, * status, 17 words for fattr, and 1 word for the byte count. */ - if (NFSSVC_MAXBLKSIZE < argp->count) { + if (NFSSVC_MAXBLKSIZE_V2 < argp->count) { printk(KERN_NOTICE "oversized read request from %u.%u.%u.%u:%d (%d bytes)\n", NIPQUAD(rqstp->rq_addr.sin_addr.s_addr), ntohs(rqstp->rq_addr.sin_port), argp->count); - argp->count = NFSSVC_MAXBLKSIZE; + argp->count = NFSSVC_MAXBLKSIZE_V2; } svc_reserve(rqstp, (19<<2) + argp->count + 4); resp->count = argp->count; nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, argp->offset, - argp->vec, argp->vlen, + rqstp->rq_vec, argp->vlen, &resp->count); if (nfserr) return nfserr; @@ -185,7 +185,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, argp->offset, - argp->vec, argp->vlen, + rqstp->rq_vec, argp->vlen, argp->len, &stable); return nfsd_return_attrs(nfserr, resp); @@ -225,7 +225,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, nfserr = nfserr_exist; if (isdotent(argp->name, argp->len)) goto done; - fh_lock(dirfhp); + fh_lock_nested(dirfhp, I_MUTEX_PARENT); dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len); if (IS_ERR(dchild)) { nfserr = nfserrno(PTR_ERR(dchild)); @@ -553,7 +553,7 @@ static struct svc_procedure nfsd_procedures2[18] = { PROC(none, void, void, none, RC_NOCACHE, ST), PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, ST+FH+AT), PROC(readlink, readlinkargs, readlinkres, none, RC_NOCACHE, ST+1+NFS_MAXPATHLEN/4), - PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE/4), + PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4), PROC(none, void, void, none, RC_NOCACHE, ST), PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, ST+AT), PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT), diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 19443056ec..6fa6340a5f 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -198,9 +198,26 @@ int nfsd_create_serv(void) unlock_kernel(); return 0; } + if (nfsd_max_blksize == 0) { + /* choose a suitable default */ + struct sysinfo i; + si_meminfo(&i); + /* Aim for 1/4096 of memory per thread + * This gives 1MB on 4Gig machines + * But only uses 32K on 128M machines. + * Bottom out at 8K on 32M and smaller. + * Of course, this is only a default. + */ + nfsd_max_blksize = NFSSVC_MAXBLKSIZE; + i.totalram <<= PAGE_SHIFT - 12; + while (nfsd_max_blksize > i.totalram && + nfsd_max_blksize >= 8*1024*2) + nfsd_max_blksize /= 2; + } atomic_set(&nfsd_busy, 0); - nfsd_serv = svc_create_pooled(&nfsd_program, NFSD_BUFSIZE, + nfsd_serv = svc_create_pooled(&nfsd_program, + NFSD_BUFSIZE - NFSSVC_MAXBLKSIZE + nfsd_max_blksize, nfsd_last_thread, nfsd, SIG_NOCLEAN, THIS_MODULE); if (nfsd_serv == NULL) diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 3f14a17eaa..1135c0d145 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -254,19 +254,18 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p, len = args->count = ntohl(*p++); p++; /* totalcount - unused */ - if (len > NFSSVC_MAXBLKSIZE) - len = NFSSVC_MAXBLKSIZE; + if (len > NFSSVC_MAXBLKSIZE_V2) + len = NFSSVC_MAXBLKSIZE_V2; /* set up somewhere to store response. * We take pages, put them on reslist and include in iovec */ v=0; while (len > 0) { - pn=rqstp->rq_resused; - svc_take_page(rqstp); - args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]); - args->vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; - len -= args->vec[v].iov_len; + pn = rqstp->rq_resused++; + rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); + rqstp->rq_vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; + len -= rqstp->rq_vec[v].iov_len; v++; } args->vlen = v; @@ -286,21 +285,21 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, args->offset = ntohl(*p++); /* offset */ p++; /* totalcount */ len = args->len = ntohl(*p++); - args->vec[0].iov_base = (void*)p; - args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - + rqstp->rq_vec[0].iov_base = (void*)p; + rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - (((void*)p) - rqstp->rq_arg.head[0].iov_base); - if (len > NFSSVC_MAXBLKSIZE) - len = NFSSVC_MAXBLKSIZE; + if (len > NFSSVC_MAXBLKSIZE_V2) + len = NFSSVC_MAXBLKSIZE_V2; v = 0; - while (len > args->vec[v].iov_len) { - len -= args->vec[v].iov_len; + while (len > rqstp->rq_vec[v].iov_len) { + len -= rqstp->rq_vec[v].iov_len; v++; - args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]); - args->vec[v].iov_len = PAGE_SIZE; + rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]); + rqstp->rq_vec[v].iov_len = PAGE_SIZE; } - args->vec[v].iov_len = len; + rqstp->rq_vec[v].iov_len = len; args->vlen = v+1; - return args->vec[0].iov_len > 0; + return rqstp->rq_vec[0].iov_len > 0; } int @@ -333,8 +332,7 @@ nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p, struct nfsd_readlinka { if (!(p = decode_fh(p, &args->fh))) return 0; - svc_take_page(rqstp); - args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -375,8 +373,7 @@ nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, if (args->count > PAGE_SIZE) args->count = PAGE_SIZE; - svc_take_page(rqstp); - args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -416,7 +413,6 @@ nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->len; if (resp->len & 3) { /* need to pad the tail */ - rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); @@ -436,7 +432,6 @@ nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->count; if (resp->count & 3) { /* need to pad the tail */ - rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3); @@ -463,7 +458,7 @@ nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p, { struct kstatfs *stat = &resp->stats; - *p++ = htonl(NFSSVC_MAXBLKSIZE); /* max transfer size */ + *p++ = htonl(NFSSVC_MAXBLKSIZE_V2); /* max transfer size */ *p++ = htonl(stat->f_bsize); *p++ = htonl(stat->f_blocks); *p++ = htonl(stat->f_bfree); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 443ebc52e3..1141bd29e4 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -54,6 +54,7 @@ #include #include #endif /* CONFIG_NFSD_V4 */ +#include #include @@ -81,10 +82,19 @@ struct raparms { dev_t p_dev; int p_set; struct file_ra_state p_ra; + unsigned int p_hindex; }; +struct raparm_hbucket { + struct raparms *pb_head; + spinlock_t pb_lock; +} ____cacheline_aligned_in_smp; + static struct raparms * raparml; -static struct raparms * raparm_cache; +#define RAPARM_HASH_BITS 4 +#define RAPARM_HASH_SIZE (1<i_mode)) { error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); if (error < 0) goto out_nfserr; @@ -743,16 +751,20 @@ nfsd_sync_dir(struct dentry *dp) * Obtain the readahead parameters for the file * specified by (dev, ino). */ -static DEFINE_SPINLOCK(ra_lock); static inline struct raparms * nfsd_get_raparms(dev_t dev, ino_t ino) { struct raparms *ra, **rap, **frap = NULL; int depth = 0; + unsigned int hash; + struct raparm_hbucket *rab; - spin_lock(&ra_lock); - for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { + hash = jhash_2words(dev, ino, 0xfeedbeef) & RAPARM_HASH_MASK; + rab = &raparm_hash[hash]; + + spin_lock(&rab->pb_lock); + for (rap = &rab->pb_head; (ra = *rap); rap = &ra->p_next) { if (ra->p_ino == ino && ra->p_dev == dev) goto found; depth++; @@ -761,7 +773,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino) } depth = nfsdstats.ra_size*11/10; if (!frap) { - spin_unlock(&ra_lock); + spin_unlock(&rab->pb_lock); return NULL; } rap = frap; @@ -769,15 +781,16 @@ nfsd_get_raparms(dev_t dev, ino_t ino) ra->p_dev = dev; ra->p_ino = ino; ra->p_set = 0; + ra->p_hindex = hash; found: - if (rap != &raparm_cache) { + if (rap != &rab->pb_head) { *rap = ra->p_next; - ra->p_next = raparm_cache; - raparm_cache = ra; + ra->p_next = rab->pb_head; + rab->pb_head = ra; } ra->p_count++; nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++; - spin_unlock(&ra_lock); + spin_unlock(&rab->pb_lock); return ra; } @@ -791,22 +804,26 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset { unsigned long count = desc->count; struct svc_rqst *rqstp = desc->arg.data; + struct page **pp = rqstp->rq_respages + rqstp->rq_resused; if (size > count) size = count; if (rqstp->rq_res.page_len == 0) { get_page(page); - rqstp->rq_respages[rqstp->rq_resused++] = page; + put_page(*pp); + *pp = page; + rqstp->rq_resused++; rqstp->rq_res.page_base = offset; rqstp->rq_res.page_len = size; - } else if (page != rqstp->rq_respages[rqstp->rq_resused-1]) { + } else if (page != pp[-1]) { get_page(page); - rqstp->rq_respages[rqstp->rq_resused++] = page; + put_page(*pp); + *pp = page; + rqstp->rq_resused++; rqstp->rq_res.page_len += size; - } else { + } else rqstp->rq_res.page_len += size; - } desc->count = count - size; desc->written += size; @@ -837,7 +854,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, file->f_ra = ra->p_ra; if (file->f_op->sendfile && rqstp->rq_sendfile_ok) { - svc_pushback_unused_pages(rqstp); + rqstp->rq_resused = 1; err = file->f_op->sendfile(file, &offset, *count, nfsd_read_actor, rqstp); } else { @@ -849,11 +866,12 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, /* Write back readahead params */ if (ra) { - spin_lock(&ra_lock); + struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex]; + spin_lock(&rab->pb_lock); ra->p_ra = file->f_ra; ra->p_set = 1; ra->p_count--; - spin_unlock(&ra_lock); + spin_unlock(&rab->pb_lock); } if (err >= 0) { @@ -1829,11 +1847,11 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) void nfsd_racache_shutdown(void) { - if (!raparm_cache) + if (!raparml) return; dprintk("nfsd: freeing readahead buffers.\n"); kfree(raparml); - raparm_cache = raparml = NULL; + raparml = NULL; } /* * Initialize readahead param cache @@ -1842,19 +1860,31 @@ int nfsd_racache_init(int cache_size) { int i; + int j = 0; + int nperbucket; + - if (raparm_cache) + if (raparml) return 0; + if (cache_size < 2*RAPARM_HASH_SIZE) + cache_size = 2*RAPARM_HASH_SIZE; raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL); if (raparml != NULL) { dprintk("nfsd: allocating %d readahead buffers.\n", cache_size); + for (i = 0 ; i < RAPARM_HASH_SIZE ; i++) { + raparm_hash[i].pb_head = NULL; + spin_lock_init(&raparm_hash[i].pb_lock); + } + nperbucket = cache_size >> RAPARM_HASH_BITS; memset(raparml, 0, sizeof(struct raparms) * cache_size); for (i = 0; i < cache_size - 1; i++) { - raparml[i].p_next = raparml + i + 1; + if (i % nperbucket == 0) + raparm_hash[j++].pb_head = raparml + i; + if (i % nperbucket < nperbucket-1) + raparml[i].p_next = raparml + i + 1; } - raparm_cache = raparml; } else { printk(KERN_WARNING "nfsd: Could not allocate memory read-ahead cache.\n"); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 7e5a2f5ebe..9c69bcacad 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1780,7 +1780,7 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th, err = -EDQUOT; goto out_end_trans; } - if (!dir || !dir->i_nlink) { + if (!dir->i_nlink) { err = -EPERM; goto out_bad_inode; } diff --git a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h index 87e5a351d8..88f02a0735 100644 --- a/include/asm-i386/hw_irq.h +++ b/include/asm-i386/hw_irq.h @@ -17,8 +17,6 @@ #include #include -struct hw_interrupt_type; - #define NMI_VECTOR 0x02 /* @@ -30,7 +28,6 @@ struct hw_interrupt_type; extern u8 irq_vector[NR_IRQ_VECTORS]; #define IO_APIC_VECTOR(irq) (irq_vector[irq]) -#define AUTO_ASSIGN -1 extern void (*interrupt[NR_IRQS])(void); diff --git a/include/asm-i386/hypertransport.h b/include/asm-i386/hypertransport.h new file mode 100644 index 0000000000..c16c6ff4bd --- /dev/null +++ b/include/asm-i386/hypertransport.h @@ -0,0 +1,42 @@ +#ifndef ASM_HYPERTRANSPORT_H +#define ASM_HYPERTRANSPORT_H + +/* + * Constants for x86 Hypertransport Interrupts. + */ + +#define HT_IRQ_LOW_BASE 0xf8000000 + +#define HT_IRQ_LOW_VECTOR_SHIFT 16 +#define HT_IRQ_LOW_VECTOR_MASK 0x00ff0000 +#define HT_IRQ_LOW_VECTOR(v) (((v) << HT_IRQ_LOW_VECTOR_SHIFT) & HT_IRQ_LOW_VECTOR_MASK) + +#define HT_IRQ_LOW_DEST_ID_SHIFT 8 +#define HT_IRQ_LOW_DEST_ID_MASK 0x0000ff00 +#define HT_IRQ_LOW_DEST_ID(v) (((v) << HT_IRQ_LOW_DEST_ID_SHIFT) & HT_IRQ_LOW_DEST_ID_MASK) + +#define HT_IRQ_LOW_DM_PHYSICAL 0x0000000 +#define HT_IRQ_LOW_DM_LOGICAL 0x0000040 + +#define HT_IRQ_LOW_RQEOI_EDGE 0x0000000 +#define HT_IRQ_LOW_RQEOI_LEVEL 0x0000020 + + +#define HT_IRQ_LOW_MT_FIXED 0x0000000 +#define HT_IRQ_LOW_MT_ARBITRATED 0x0000004 +#define HT_IRQ_LOW_MT_SMI 0x0000008 +#define HT_IRQ_LOW_MT_NMI 0x000000c +#define HT_IRQ_LOW_MT_INIT 0x0000010 +#define HT_IRQ_LOW_MT_STARTUP 0x0000014 +#define HT_IRQ_LOW_MT_EXTINT 0x0000018 +#define HT_IRQ_LOW_MT_LINT1 0x000008c +#define HT_IRQ_LOW_MT_LINT0 0x0000098 + +#define HT_IRQ_LOW_IRQ_MASKED 0x0000001 + + +#define HT_IRQ_HIGH_DEST_ID_SHIFT 0 +#define HT_IRQ_HIGH_DEST_ID_MASK 0x00ffffff +#define HT_IRQ_HIGH_DEST_ID(v) ((((v) >> 8) << HT_IRQ_HIGH_DEST_ID_SHIFT) & HT_IRQ_HIGH_DEST_ID_MASK) + +#endif /* ASM_HYPERTRANSPORT_H */ diff --git a/include/asm-i386/io_apic.h b/include/asm-i386/io_apic.h index 5d309275a1..276ea7e814 100644 --- a/include/asm-i386/io_apic.h +++ b/include/asm-i386/io_apic.h @@ -12,46 +12,6 @@ #ifdef CONFIG_X86_IO_APIC -#ifdef CONFIG_PCI_MSI -static inline int use_pci_vector(void) {return 1;} -static inline void disable_edge_ioapic_vector(unsigned int vector) { } -static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { } -static inline void end_edge_ioapic_vector (unsigned int vector) { } -#define startup_level_ioapic startup_level_ioapic_vector -#define shutdown_level_ioapic mask_IO_APIC_vector -#define enable_level_ioapic unmask_IO_APIC_vector -#define disable_level_ioapic mask_IO_APIC_vector -#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_vector -#define end_level_ioapic end_level_ioapic_vector -#define set_ioapic_affinity set_ioapic_affinity_vector - -#define startup_edge_ioapic startup_edge_ioapic_vector -#define shutdown_edge_ioapic disable_edge_ioapic_vector -#define enable_edge_ioapic unmask_IO_APIC_vector -#define disable_edge_ioapic disable_edge_ioapic_vector -#define ack_edge_ioapic ack_edge_ioapic_vector -#define end_edge_ioapic end_edge_ioapic_vector -#else -static inline int use_pci_vector(void) {return 0;} -static inline void disable_edge_ioapic_irq(unsigned int irq) { } -static inline void mask_and_ack_level_ioapic_irq(unsigned int irq) { } -static inline void end_edge_ioapic_irq (unsigned int irq) { } -#define startup_level_ioapic startup_level_ioapic_irq -#define shutdown_level_ioapic mask_IO_APIC_irq -#define enable_level_ioapic unmask_IO_APIC_irq -#define disable_level_ioapic mask_IO_APIC_irq -#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_irq -#define end_level_ioapic end_level_ioapic_irq -#define set_ioapic_affinity set_ioapic_affinity_irq - -#define startup_edge_ioapic startup_edge_ioapic_irq -#define shutdown_edge_ioapic disable_edge_ioapic_irq -#define enable_edge_ioapic unmask_IO_APIC_irq -#define disable_edge_ioapic disable_edge_ioapic_irq -#define ack_edge_ioapic ack_edge_ioapic_irq -#define end_edge_ioapic end_edge_ioapic_irq -#endif - #define IO_APIC_BASE(idx) \ ((volatile int *)(__fix_to_virt(FIX_IO_APIC_BASE_0 + idx) \ + (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK))) @@ -219,6 +179,4 @@ extern int (*ioapic_renumber_irq)(int ioapic, int irq); static inline void disable_ioapic_setup(void) { } #endif -extern int assign_irq_vector(int irq); - #endif diff --git a/include/asm-i386/mach-default/irq_vectors_limits.h b/include/asm-i386/mach-default/irq_vectors_limits.h index b330026e6f..7f161e760b 100644 --- a/include/asm-i386/mach-default/irq_vectors_limits.h +++ b/include/asm-i386/mach-default/irq_vectors_limits.h @@ -1,10 +1,6 @@ #ifndef _ASM_IRQ_VECTORS_LIMITS_H #define _ASM_IRQ_VECTORS_LIMITS_H -#ifdef CONFIG_PCI_MSI -#define NR_IRQS FIRST_SYSTEM_VECTOR -#define NR_IRQ_VECTORS NR_IRQS -#else #ifdef CONFIG_X86_IO_APIC #define NR_IRQS 224 # if (224 >= 32 * NR_CPUS) @@ -16,6 +12,5 @@ #define NR_IRQS 16 #define NR_IRQ_VECTORS NR_IRQS #endif -#endif #endif /* _ASM_IRQ_VECTORS_LIMITS_H */ diff --git a/include/asm-i386/msi.h b/include/asm-i386/msi.h deleted file mode 100644 index b11c4b7dfa..0000000000 --- a/include/asm-i386/msi.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2003-2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef ASM_MSI_H -#define ASM_MSI_H - -#include -#include - -#define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) -#define MSI_TARGET_CPU_SHIFT 12 - -extern struct msi_ops msi_apic_ops; - -static inline int msi_arch_init(void) -{ - msi_register(&msi_apic_ops); - return 0; -} - -#endif /* ASM_MSI_H */ diff --git a/include/asm-i386/msidef.h b/include/asm-i386/msidef.h new file mode 100644 index 0000000000..5b8acddb70 --- /dev/null +++ b/include/asm-i386/msidef.h @@ -0,0 +1,47 @@ +#ifndef ASM_MSIDEF_H +#define ASM_MSIDEF_H + +/* + * Constants for Intel APIC based MSI messages. + */ + +/* + * Shifts for MSI data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff +#define MSI_DATA_VECTOR(v) (((v) << MSI_DATA_VECTOR_SHIFT) & MSI_DATA_VECTOR_MASK) + +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_MODE_SHIFT) +#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_MODE_SHIFT) + +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) +#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) + +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) +#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) + +/* + * Shift/mask fields for msi address + */ + +#define MSI_ADDR_BASE_HI 0 +#define MSI_ADDR_BASE_LO 0xfee00000 + +#define MSI_ADDR_DEST_MODE_SHIFT 2 +#define MSI_ADDR_DEST_MODE_PHYSICAL (0 << MSI_ADDR_DEST_MODE_SHIFT) +#define MSI_ADDR_DEST_MODE_LOGICAL (1 << MSI_ADDR_DEST_MODE_SHIFT) + +#define MSI_ADDR_REDIRECTION_SHIFT 3 +#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) /* dedicated cpu */ +#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) /* lowest priority */ + +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 +#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & MSI_ADDR_DEST_ID_MASK) + +#endif /* ASM_MSIDEF_H */ diff --git a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h index 15b545a897..90cba967df 100644 --- a/include/asm-ia64/machvec.h +++ b/include/asm-ia64/machvec.h @@ -20,6 +20,7 @@ struct page; struct mm_struct; struct pci_bus; struct task_struct; +struct pci_dev; typedef void ia64_mv_setup_t (char **); typedef void ia64_mv_cpu_init_t (void); @@ -75,7 +76,9 @@ typedef unsigned char ia64_mv_readb_relaxed_t (const volatile void __iomem *); typedef unsigned short ia64_mv_readw_relaxed_t (const volatile void __iomem *); typedef unsigned int ia64_mv_readl_relaxed_t (const volatile void __iomem *); typedef unsigned long ia64_mv_readq_relaxed_t (const volatile void __iomem *); -typedef int ia64_mv_msi_init_t (void); + +typedef int ia64_mv_setup_msi_irq_t (unsigned int irq, struct pci_dev *pdev); +typedef void ia64_mv_teardown_msi_irq_t (unsigned int irq); static inline void machvec_noop (void) @@ -154,7 +157,8 @@ extern void machvec_tlb_migrate_finish (struct mm_struct *); # define platform_readl_relaxed ia64_mv.readl_relaxed # define platform_readq_relaxed ia64_mv.readq_relaxed # define platform_migrate ia64_mv.migrate -# define platform_msi_init ia64_mv.msi_init +# define platform_setup_msi_irq ia64_mv.setup_msi_irq +# define platform_teardown_msi_irq ia64_mv.teardown_msi_irq # endif /* __attribute__((__aligned__(16))) is required to make size of the @@ -204,7 +208,8 @@ struct ia64_machine_vector { ia64_mv_readl_relaxed_t *readl_relaxed; ia64_mv_readq_relaxed_t *readq_relaxed; ia64_mv_migrate_t *migrate; - ia64_mv_msi_init_t *msi_init; + ia64_mv_setup_msi_irq_t *setup_msi_irq; + ia64_mv_teardown_msi_irq_t *teardown_msi_irq; } __attribute__((__aligned__(16))); /* align attrib? see above comment */ #define MACHVEC_INIT(name) \ @@ -250,7 +255,8 @@ struct ia64_machine_vector { platform_readl_relaxed, \ platform_readq_relaxed, \ platform_migrate, \ - platform_msi_init, \ + platform_setup_msi_irq, \ + platform_teardown_msi_irq, \ } extern struct ia64_machine_vector ia64_mv; @@ -404,8 +410,11 @@ extern int ia64_pci_legacy_write(struct pci_bus *bus, u16 port, u32 val, u8 size #ifndef platform_migrate # define platform_migrate machvec_noop_task #endif -#ifndef platform_msi_init -# define platform_msi_init ((ia64_mv_msi_init_t*)NULL) +#ifndef platform_setup_msi_irq +# define platform_setup_msi_irq ((ia64_mv_setup_msi_irq_t*)NULL) +#endif +#ifndef platform_teardown_msi_irq +# define platform_teardown_msi_irq ((ia64_mv_teardown_msi_irq_t*)NULL) #endif #endif /* _ASM_IA64_MACHVEC_H */ diff --git a/include/asm-ia64/machvec_sn2.h b/include/asm-ia64/machvec_sn2.h index cf724dc79d..c54b165b1c 100644 --- a/include/asm-ia64/machvec_sn2.h +++ b/include/asm-ia64/machvec_sn2.h @@ -67,7 +67,8 @@ extern ia64_mv_dma_sync_sg_for_device sn_dma_sync_sg_for_device; extern ia64_mv_dma_mapping_error sn_dma_mapping_error; extern ia64_mv_dma_supported sn_dma_supported; extern ia64_mv_migrate_t sn_migrate; -extern ia64_mv_msi_init_t sn_msi_init; +extern ia64_mv_setup_msi_irq_t sn_setup_msi_irq; +extern ia64_mv_teardown_msi_irq_t sn_teardown_msi_irq; /* @@ -120,9 +121,11 @@ extern ia64_mv_msi_init_t sn_msi_init; #define platform_dma_supported sn_dma_supported #define platform_migrate sn_migrate #ifdef CONFIG_PCI_MSI -#define platform_msi_init sn_msi_init +#define platform_setup_msi_irq sn_setup_msi_irq +#define platform_teardown_msi_irq sn_teardown_msi_irq #else -#define platform_msi_init ((ia64_mv_msi_init_t*)NULL) +#define platform_setup_msi_irq ((ia64_mv_setup_msi_irq_t*)NULL) +#define platform_teardown_msi_irq ((ia64_mv_teardown_msi_irq_t*)NULL) #endif #include diff --git a/include/asm-ia64/msi.h b/include/asm-ia64/msi.h deleted file mode 100644 index bb92b0dbde..0000000000 --- a/include/asm-ia64/msi.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2003-2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef ASM_MSI_H -#define ASM_MSI_H - -#define NR_VECTORS NR_IRQS -#define FIRST_DEVICE_VECTOR IA64_FIRST_DEVICE_VECTOR -#define LAST_DEVICE_VECTOR IA64_LAST_DEVICE_VECTOR -static inline void set_intr_gate (int nr, void *func) {} -#define IO_APIC_VECTOR(irq) (irq) -#define ack_APIC_irq ia64_eoi -#define MSI_TARGET_CPU_SHIFT 4 - -extern struct msi_ops msi_apic_ops; - -static inline int msi_arch_init(void) -{ - if (platform_msi_init) - return platform_msi_init(); - - /* default ops for most ia64 platforms */ - msi_register(&msi_apic_ops); - return 0; -} - -#endif /* ASM_MSI_H */ diff --git a/include/asm-x86_64/hardirq.h b/include/asm-x86_64/hardirq.h index 64a65ce2f4..95d5e090ed 100644 --- a/include/asm-x86_64/hardirq.h +++ b/include/asm-x86_64/hardirq.h @@ -6,6 +6,9 @@ #include #include +/* We can have at most NR_VECTORS irqs routed to a cpu at a time */ +#define MAX_HARDIRQS_PER_CPU NR_VECTORS + #define __ARCH_IRQ_STAT 1 #define local_softirq_pending() read_pda(__softirq_pending) diff --git a/include/asm-x86_64/hw_irq.h b/include/asm-x86_64/hw_irq.h index 48a4a5364e..53d0d9fd10 100644 --- a/include/asm-x86_64/hw_irq.h +++ b/include/asm-x86_64/hw_irq.h @@ -19,8 +19,7 @@ #include #include #include - -struct hw_interrupt_type; +#include #endif #define NMI_VECTOR 0x02 @@ -75,9 +74,10 @@ struct hw_interrupt_type; #ifndef __ASSEMBLY__ -extern u8 irq_vector[NR_IRQ_VECTORS]; +extern unsigned int irq_vector[NR_IRQ_VECTORS]; +typedef int vector_irq_t[NR_VECTORS]; +DECLARE_PER_CPU(vector_irq_t, vector_irq); #define IO_APIC_VECTOR(irq) (irq_vector[irq]) -#define AUTO_ASSIGN -1 /* * Various low-level irq details needed by irq.c, process.c, diff --git a/include/asm-x86_64/hypertransport.h b/include/asm-x86_64/hypertransport.h new file mode 100644 index 0000000000..c16c6ff4bd --- /dev/null +++ b/include/asm-x86_64/hypertransport.h @@ -0,0 +1,42 @@ +#ifndef ASM_HYPERTRANSPORT_H +#define ASM_HYPERTRANSPORT_H + +/* + * Constants for x86 Hypertransport Interrupts. + */ + +#define HT_IRQ_LOW_BASE 0xf8000000 + +#define HT_IRQ_LOW_VECTOR_SHIFT 16 +#define HT_IRQ_LOW_VECTOR_MASK 0x00ff0000 +#define HT_IRQ_LOW_VECTOR(v) (((v) << HT_IRQ_LOW_VECTOR_SHIFT) & HT_IRQ_LOW_VECTOR_MASK) + +#define HT_IRQ_LOW_DEST_ID_SHIFT 8 +#define HT_IRQ_LOW_DEST_ID_MASK 0x0000ff00 +#define HT_IRQ_LOW_DEST_ID(v) (((v) << HT_IRQ_LOW_DEST_ID_SHIFT) & HT_IRQ_LOW_DEST_ID_MASK) + +#define HT_IRQ_LOW_DM_PHYSICAL 0x0000000 +#define HT_IRQ_LOW_DM_LOGICAL 0x0000040 + +#define HT_IRQ_LOW_RQEOI_EDGE 0x0000000 +#define HT_IRQ_LOW_RQEOI_LEVEL 0x0000020 + + +#define HT_IRQ_LOW_MT_FIXED 0x0000000 +#define HT_IRQ_LOW_MT_ARBITRATED 0x0000004 +#define HT_IRQ_LOW_MT_SMI 0x0000008 +#define HT_IRQ_LOW_MT_NMI 0x000000c +#define HT_IRQ_LOW_MT_INIT 0x0000010 +#define HT_IRQ_LOW_MT_STARTUP 0x0000014 +#define HT_IRQ_LOW_MT_EXTINT 0x0000018 +#define HT_IRQ_LOW_MT_LINT1 0x000008c +#define HT_IRQ_LOW_MT_LINT0 0x0000098 + +#define HT_IRQ_LOW_IRQ_MASKED 0x0000001 + + +#define HT_IRQ_HIGH_DEST_ID_SHIFT 0 +#define HT_IRQ_HIGH_DEST_ID_MASK 0x00ffffff +#define HT_IRQ_HIGH_DEST_ID(v) ((((v) >> 8) << HT_IRQ_HIGH_DEST_ID_SHIFT) & HT_IRQ_HIGH_DEST_ID_MASK) + +#endif /* ASM_HYPERTRANSPORT_H */ diff --git a/include/asm-x86_64/io_apic.h b/include/asm-x86_64/io_apic.h index 5d1b5c68e3..171ec2dc8c 100644 --- a/include/asm-x86_64/io_apic.h +++ b/include/asm-x86_64/io_apic.h @@ -10,46 +10,6 @@ * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar */ -#ifdef CONFIG_PCI_MSI -static inline int use_pci_vector(void) {return 1;} -static inline void disable_edge_ioapic_vector(unsigned int vector) { } -static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { } -static inline void end_edge_ioapic_vector (unsigned int vector) { } -#define startup_level_ioapic startup_level_ioapic_vector -#define shutdown_level_ioapic mask_IO_APIC_vector -#define enable_level_ioapic unmask_IO_APIC_vector -#define disable_level_ioapic mask_IO_APIC_vector -#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_vector -#define end_level_ioapic end_level_ioapic_vector -#define set_ioapic_affinity set_ioapic_affinity_vector - -#define startup_edge_ioapic startup_edge_ioapic_vector -#define shutdown_edge_ioapic disable_edge_ioapic_vector -#define enable_edge_ioapic unmask_IO_APIC_vector -#define disable_edge_ioapic disable_edge_ioapic_vector -#define ack_edge_ioapic ack_edge_ioapic_vector -#define end_edge_ioapic end_edge_ioapic_vector -#else -static inline int use_pci_vector(void) {return 0;} -static inline void disable_edge_ioapic_irq(unsigned int irq) { } -static inline void mask_and_ack_level_ioapic_irq(unsigned int irq) { } -static inline void end_edge_ioapic_irq (unsigned int irq) { } -#define startup_level_ioapic startup_level_ioapic_irq -#define shutdown_level_ioapic mask_IO_APIC_irq -#define enable_level_ioapic unmask_IO_APIC_irq -#define disable_level_ioapic mask_IO_APIC_irq -#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_irq -#define end_level_ioapic end_level_ioapic_irq -#define set_ioapic_affinity set_ioapic_affinity_irq - -#define startup_edge_ioapic startup_edge_ioapic_irq -#define shutdown_edge_ioapic disable_edge_ioapic_irq -#define enable_edge_ioapic unmask_IO_APIC_irq -#define disable_edge_ioapic disable_edge_ioapic_irq -#define ack_edge_ioapic ack_edge_ioapic_irq -#define end_edge_ioapic end_edge_ioapic_irq -#endif - #define APIC_MISMATCH_DEBUG #define IO_APIC_BASE(idx) \ @@ -202,13 +162,10 @@ extern int skip_ioapic_setup; extern int io_apic_get_version (int ioapic); extern int io_apic_get_redir_entries (int ioapic); extern int io_apic_set_pci_routing (int ioapic, int pin, int irq, int, int); -extern int timer_uses_ioapic_pin_0; #endif extern int sis_apic_bug; /* dummy */ -extern int assign_irq_vector(int irq); - void enable_NMI_through_LVT0 (void * dummy); extern spinlock_t i8259A_lock; diff --git a/include/asm-x86_64/irq.h b/include/asm-x86_64/irq.h index 43469d8ab7..5006c6e756 100644 --- a/include/asm-x86_64/irq.h +++ b/include/asm-x86_64/irq.h @@ -31,13 +31,8 @@ #define FIRST_SYSTEM_VECTOR 0xef /* duplicated in hw_irq.h */ -#ifdef CONFIG_PCI_MSI -#define NR_IRQS FIRST_SYSTEM_VECTOR +#define NR_IRQS (NR_VECTORS + (32 *NR_CPUS)) #define NR_IRQ_VECTORS NR_IRQS -#else -#define NR_IRQS 224 -#define NR_IRQ_VECTORS (32 * NR_CPUS) -#endif static __inline__ int irq_canonicalize(int irq) { diff --git a/include/asm-x86_64/msi.h b/include/asm-x86_64/msi.h deleted file mode 100644 index 3ad2346624..0000000000 --- a/include/asm-x86_64/msi.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2003-2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef ASM_MSI_H -#define ASM_MSI_H - -#include -#include -#include - -#define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) -#define MSI_TARGET_CPU_SHIFT 12 - -extern struct msi_ops msi_apic_ops; - -static inline int msi_arch_init(void) -{ - msi_register(&msi_apic_ops); - return 0; -} - -#endif /* ASM_MSI_H */ diff --git a/include/asm-x86_64/msidef.h b/include/asm-x86_64/msidef.h new file mode 100644 index 0000000000..5b8acddb70 --- /dev/null +++ b/include/asm-x86_64/msidef.h @@ -0,0 +1,47 @@ +#ifndef ASM_MSIDEF_H +#define ASM_MSIDEF_H + +/* + * Constants for Intel APIC based MSI messages. + */ + +/* + * Shifts for MSI data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff +#define MSI_DATA_VECTOR(v) (((v) << MSI_DATA_VECTOR_SHIFT) & MSI_DATA_VECTOR_MASK) + +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_MODE_SHIFT) +#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_MODE_SHIFT) + +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) +#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) + +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) +#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) + +/* + * Shift/mask fields for msi address + */ + +#define MSI_ADDR_BASE_HI 0 +#define MSI_ADDR_BASE_LO 0xfee00000 + +#define MSI_ADDR_DEST_MODE_SHIFT 2 +#define MSI_ADDR_DEST_MODE_PHYSICAL (0 << MSI_ADDR_DEST_MODE_SHIFT) +#define MSI_ADDR_DEST_MODE_LOGICAL (1 << MSI_ADDR_DEST_MODE_SHIFT) + +#define MSI_ADDR_REDIRECTION_SHIFT 3 +#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) /* dedicated cpu */ +#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) /* lowest priority */ + +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 +#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & MSI_ADDR_DEST_ID_MASK) + +#endif /* ASM_MSIDEF_H */ diff --git a/include/linux/Kbuild b/include/linux/Kbuild index f7a52e19b4..7d564b6fc9 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -156,12 +156,10 @@ header-y += toshiba.h header-y += ultrasound.h header-y += un.h header-y += utime.h -header-y += utsname.h header-y += video_decoder.h header-y += video_encoder.h header-y += videotext.h header-y += vt.h -header-y += wavefront.h header-y += wireless.h header-y += xattr.h header-y += x25.h @@ -333,6 +331,7 @@ unifdef-y += unistd.h unifdef-y += usb_ch9.h unifdef-y += usbdevice_fs.h unifdef-y += user.h +unifdef-y += utsname.h unifdef-y += videodev2.h unifdef-y += videodev.h unifdef-y += wait.h diff --git a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h index 2ed2fd8551..22eb936723 100644 --- a/include/linux/ac97_codec.h +++ b/include/linux/ac97_codec.h @@ -331,8 +331,6 @@ extern int ac97_read_proc (char *page_out, char **start, off_t off, extern int ac97_probe_codec(struct ac97_codec *); extern unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate); extern unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate); -extern int ac97_save_state(struct ac97_codec *codec); -extern int ac97_restore_state(struct ac97_codec *codec); extern struct ac97_codec *ac97_alloc_codec(void); extern void ac97_release_codec(struct ac97_codec *codec); @@ -346,9 +344,6 @@ struct ac97_driver { void (*remove) (struct ac97_codec *codec, struct ac97_driver *driver); }; -extern int ac97_register_driver(struct ac97_driver *driver); -extern void ac97_unregister_driver(struct ac97_driver *driver); - /* quirk types */ enum { AC97_TUNE_DEFAULT = -1, /* use default from quirk list (not valid in list) */ diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 50d8b5744c..612472aaa7 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -28,11 +28,16 @@ #ifndef HARDIRQ_BITS #define HARDIRQ_BITS 12 + +#ifndef MAX_HARDIRQS_PER_CPU +#define MAX_HARDIRQS_PER_CPU NR_IRQS +#endif + /* * The hardirq mask has to be large enough to have space for potentially * all IRQ sources in the system nesting on a single CPU. */ -#if (1 << HARDIRQ_BITS) < NR_IRQS +#if (1 << HARDIRQ_BITS) < MAX_HARDIRQS_PER_CPU # error HARDIRQ_BITS is too low! #endif #endif diff --git a/include/linux/htirq.h b/include/linux/htirq.h new file mode 100644 index 0000000000..1f15ce279a --- /dev/null +++ b/include/linux/htirq.h @@ -0,0 +1,15 @@ +#ifndef LINUX_HTIRQ_H +#define LINUX_HTIRQ_H + +/* Helper functions.. */ +void write_ht_irq_low(unsigned int irq, u32 data); +void write_ht_irq_high(unsigned int irq, u32 data); +u32 read_ht_irq_low(unsigned int irq); +u32 read_ht_irq_high(unsigned int irq); +void mask_ht_irq(unsigned int irq); +void unmask_ht_irq(unsigned int irq); + +/* The arch hook for getting things started */ +int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev); + +#endif /* LINUX_HTIRQ_H */ diff --git a/include/linux/ipc.h b/include/linux/ipc.h index d9e2b3f36c..636094c29b 100644 --- a/include/linux/ipc.h +++ b/include/linux/ipc.h @@ -2,7 +2,6 @@ #define _LINUX_IPC_H #include -#include #define IPC_PRIVATE ((__kernel_key_t) 0) @@ -52,6 +51,8 @@ struct ipc_perm #ifdef __KERNEL__ +#include + #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ /* used by in-kernel data structures */ diff --git a/include/linux/irq.h b/include/linux/irq.h index 48d3cb3b6a..6f463606c3 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -59,6 +59,7 @@ #define IRQ_NOAUTOEN 0x08000000 /* IRQ will not be enabled on request irq */ #define IRQ_DELAYED_DISABLE 0x10000000 /* IRQ disable (masking) happens delayed. */ #define IRQ_WAKEUP 0x20000000 /* IRQ triggers system wakeup */ +#define IRQ_MOVE_PENDING 0x40000000 /* need to re-target IRQ destination */ struct proc_dir_entry; @@ -132,7 +133,6 @@ struct irq_chip { * @affinity: IRQ affinity on SMP * @cpu: cpu index useful for balancing * @pending_mask: pending rebalanced interrupts - * @move_irq: need to re-target IRQ destination * @dir: /proc/irq/ procfs entry * @affinity_entry: /proc/irq/smp_affinity procfs entry on SMP * @@ -159,7 +159,6 @@ struct irq_desc { #endif #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) cpumask_t pending_mask; - unsigned int move_irq; /* need to re-target IRQ dest */ #endif #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; @@ -206,36 +205,7 @@ static inline void set_native_irq_info(int irq, cpumask_t mask) void set_pending_irq(unsigned int irq, cpumask_t mask); void move_native_irq(int irq); - -#ifdef CONFIG_PCI_MSI -/* - * Wonder why these are dummies? - * For e.g the set_ioapic_affinity_vector() calls the set_ioapic_affinity_irq() - * counter part after translating the vector to irq info. We need to perform - * this operation on the real irq, when we dont use vector, i.e when - * pci_use_vector() is false. - */ -static inline void move_irq(int irq) -{ -} - -static inline void set_irq_info(int irq, cpumask_t mask) -{ -} - -#else /* CONFIG_PCI_MSI */ - -static inline void move_irq(int irq) -{ - move_native_irq(irq); -} - -static inline void set_irq_info(int irq, cpumask_t mask) -{ - set_native_irq_info(irq, mask); -} - -#endif /* CONFIG_PCI_MSI */ +void move_masked_irq(int irq); #else /* CONFIG_GENERIC_PENDING_IRQ || CONFIG_IRQBALANCE */ @@ -247,21 +217,20 @@ static inline void move_native_irq(int irq) { } -static inline void set_pending_irq(unsigned int irq, cpumask_t mask) +static inline void move_masked_irq(int irq) { } -static inline void set_irq_info(int irq, cpumask_t mask) +static inline void set_pending_irq(unsigned int irq, cpumask_t mask) { - set_native_irq_info(irq, mask); } #endif /* CONFIG_GENERIC_PENDING_IRQ */ #else /* CONFIG_SMP */ -#define move_irq(x) #define move_native_irq(x) +#define move_masked_irq(x) #endif /* CONFIG_SMP */ @@ -399,8 +368,22 @@ set_irq_chained_handler(unsigned int irq, __set_irq_handler(irq, handle, 1); } -/* Set/get chip/data for an IRQ: */ +/* Handle dynamic irq creation and destruction */ +extern int create_irq(void); +extern void destroy_irq(unsigned int irq); +/* Test to see if a driver has successfully requested an irq */ +static inline int irq_has_action(unsigned int irq) +{ + struct irq_desc *desc = irq_desc + irq; + return desc->action != NULL; +} + +/* Dynamic irq helper functions */ +extern void dynamic_irq_init(unsigned int irq); +extern void dynamic_irq_cleanup(unsigned int irq); + +/* Set/get chip/data for an IRQ: */ extern int set_irq_chip(unsigned int irq, struct irq_chip *chip); extern int set_irq_data(unsigned int irq, void *data); extern int set_irq_chip_data(unsigned int irq, void *data); diff --git a/include/linux/libata.h b/include/linux/libata.h index d6a3d4b345..d1af1dbeae 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -109,6 +109,10 @@ static inline u32 ata_msg_init(int dval, int default_msg_enable_bits) #define ATA_TAG_POISON 0xfafbfcfdU /* move to PCI layer? */ +#define PCI_VDEVICE(vendor, device) \ + PCI_VENDOR_ID_##vendor, (device), \ + PCI_ANY_ID, PCI_ANY_ID, 0, 0 + static inline struct device *pci_dev_to_dev(struct pci_dev *pdev) { return &pdev->dev; @@ -138,8 +142,9 @@ enum { ATA_DFLAG_NCQ = (1 << 3), /* device supports NCQ */ ATA_DFLAG_CFG_MASK = (1 << 8) - 1, - ATA_DFLAG_PIO = (1 << 8), /* device currently in PIO mode */ - ATA_DFLAG_SUSPENDED = (1 << 9), /* device suspended */ + ATA_DFLAG_PIO = (1 << 8), /* device limited to PIO mode */ + ATA_DFLAG_NCQ_OFF = (1 << 9), /* devied limited to non-NCQ mode */ + ATA_DFLAG_SUSPENDED = (1 << 10), /* device suspended */ ATA_DFLAG_INIT_MASK = (1 << 16) - 1, ATA_DFLAG_DETACH = (1 << 16), diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 47b7dbd647..2909619c02 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -37,17 +37,15 @@ * Lockd host handle (used both by the client and server personality). */ struct nlm_host { - struct nlm_host * h_next; /* linked list (hash table) */ + struct hlist_node h_hash; /* doubly linked list */ struct sockaddr_in h_addr; /* peer address */ struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ - char h_name[20]; /* remote hostname */ + char * h_name; /* remote hostname */ u32 h_version; /* interface version */ unsigned short h_proto; /* transport proto */ unsigned short h_reclaiming : 1, h_server : 1, /* server side, not client side */ - h_inuse : 1, - h_killed : 1, - h_monitored : 1; + h_inuse : 1; wait_queue_head_t h_gracewait; /* wait while reclaiming */ struct rw_semaphore h_rwsem; /* Reboot recovery lock */ u32 h_state; /* pseudo-state counter */ @@ -61,6 +59,16 @@ struct nlm_host { spinlock_t h_lock; struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ + struct nsm_handle * h_nsmhandle; /* NSM status handle */ +}; + +struct nsm_handle { + struct list_head sm_link; + atomic_t sm_count; + char * sm_name; + struct sockaddr_in sm_addr; + unsigned int sm_monitored : 1, + sm_sticky : 1; /* don't unmonitor */ }; /* @@ -96,15 +104,14 @@ struct nlm_rqst { * an NFS client. */ struct nlm_file { - struct nlm_file * f_next; /* linked list */ + struct hlist_node f_list; /* linked list */ struct nfs_fh f_handle; /* NFS file handle */ struct file * f_file; /* VFS file pointer */ struct nlm_share * f_shares; /* DOS shares */ - struct nlm_block * f_blocks; /* blocked locks */ + struct list_head f_blocks; /* blocked locks */ unsigned int f_locks; /* guesstimate # of locks */ unsigned int f_count; /* reference count */ - struct semaphore f_sema; /* avoid concurrent access */ - int f_hash; /* hash of f_handle */ + struct mutex f_mutex; /* avoid concurrent access */ }; /* @@ -114,25 +121,17 @@ struct nlm_file { #define NLM_NEVER (~(unsigned long) 0) struct nlm_block { struct kref b_count; /* Reference count */ - struct nlm_block * b_next; /* linked list (all blocks) */ - struct nlm_block * b_fnext; /* linked list (per file) */ + struct list_head b_list; /* linked list of all blocks */ + struct list_head b_flist; /* linked list (per file) */ struct nlm_rqst * b_call; /* RPC args & callback info */ struct svc_serv * b_daemon; /* NLM service */ struct nlm_host * b_host; /* host handle for RPC clnt */ unsigned long b_when; /* next re-xmit */ unsigned int b_id; /* block id */ - unsigned char b_queued; /* re-queued */ unsigned char b_granted; /* VFS granted lock */ struct nlm_file * b_file; /* file in question */ }; -/* - * Valid actions for nlmsvc_traverse_files - */ -#define NLM_ACT_CHECK 0 /* check for locks */ -#define NLM_ACT_MARK 1 /* mark & sweep */ -#define NLM_ACT_UNLOCK 2 /* release all locks */ - /* * Global variables */ @@ -143,6 +142,7 @@ extern struct svc_procedure nlmsvc_procedures4[]; #endif extern int nlmsvc_grace_period; extern unsigned long nlmsvc_timeout; +extern int nsm_use_hostnames; /* * Lockd client functions @@ -155,22 +155,31 @@ struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock void nlmclnt_finish_block(struct nlm_wait *block); int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *); -void nlmclnt_recovery(struct nlm_host *, u32); +void nlmclnt_recovery(struct nlm_host *); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); +void nlmclnt_next_cookie(struct nlm_cookie *); /* * Host cache */ -struct nlm_host * nlmclnt_lookup_host(struct sockaddr_in *, int, int); -struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *); -struct nlm_host * nlm_lookup_host(int server, struct sockaddr_in *, int, int); +struct nlm_host * nlmclnt_lookup_host(const struct sockaddr_in *, int, int, const char *, int); +struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *, const char *, int); +struct nlm_host * nlm_lookup_host(int server, const struct sockaddr_in *, int, int, const char *, int); struct rpc_clnt * nlm_bind_host(struct nlm_host *); void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *); void nlm_release_host(struct nlm_host *); void nlm_shutdown_hosts(void); -extern struct nlm_host *nlm_find_client(void); +extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, int, u32); +struct nsm_handle *nsm_find(const struct sockaddr_in *, const char *, int); +void nsm_release(struct nsm_handle *); + +/* + * This is used in garbage collection and resource reclaim + * A return value != 0 means destroy the lock/block/share + */ +typedef int (*nlm_host_match_fn_t)(struct nlm_host *cur, struct nlm_host *ref); /* * Server-side lock handling @@ -183,8 +192,8 @@ u32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); unsigned long nlmsvc_retry_blocked(void); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, - int action); -void nlmsvc_grant_reply(struct svc_rqst *, struct nlm_cookie *, u32); + nlm_host_match_fn_t match); +void nlmsvc_grant_reply(struct nlm_cookie *, u32); /* * File handling for the server personality diff --git a/include/linux/lockd/share.h b/include/linux/lockd/share.h index c75a424ebe..cd7816e74c 100644 --- a/include/linux/lockd/share.h +++ b/include/linux/lockd/share.h @@ -25,6 +25,7 @@ u32 nlmsvc_share_file(struct nlm_host *, struct nlm_file *, struct nlm_args *); u32 nlmsvc_unshare_file(struct nlm_host *, struct nlm_file *, struct nlm_args *); -void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, int); +void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, + nlm_host_match_fn_t); #endif /* LINUX_LOCKD_SHARE_H */ diff --git a/include/linux/lockd/sm_inter.h b/include/linux/lockd/sm_inter.h index 1080bb6ae3..fc61d40964 100644 --- a/include/linux/lockd/sm_inter.h +++ b/include/linux/lockd/sm_inter.h @@ -28,7 +28,8 @@ struct nsm_args { u32 prog; /* RPC callback info */ u32 vers; u32 proc; - u32 proto; /* protocol (udp/tcp) plus server/client flag */ + + char * mon_name; }; /* @@ -41,6 +42,6 @@ struct nsm_res { int nsm_monitor(struct nlm_host *); int nsm_unmonitor(struct nlm_host *); -extern u32 nsm_local_state; +extern int nsm_local_state; #endif /* LINUX_LOCKD_SM_INTER_H */ diff --git a/include/linux/msi.h b/include/linux/msi.h new file mode 100644 index 0000000000..c7ef943436 --- /dev/null +++ b/include/linux/msi.h @@ -0,0 +1,49 @@ +#ifndef LINUX_MSI_H +#define LINUX_MSI_H + +struct msi_msg { + u32 address_lo; /* low 32 bits of msi message address */ + u32 address_hi; /* high 32 bits of msi message address */ + u32 data; /* 16 bits of msi message data */ +}; + +/* Heper functions */ +extern void mask_msi_irq(unsigned int irq); +extern void unmask_msi_irq(unsigned int irq); +extern void read_msi_msg(unsigned int irq, struct msi_msg *msg); + +extern void write_msi_msg(unsigned int irq, struct msi_msg *msg); + +struct msi_desc { + struct { + __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ + __u8 maskbit : 1; /* mask-pending bit supported ? */ + __u8 unused : 1; + __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ + __u8 pos; /* Location of the msi capability */ + __u16 entry_nr; /* specific enabled entry */ + unsigned default_irq; /* default pre-assigned irq */ + }msi_attrib; + + struct { + __u16 head; + __u16 tail; + }link; + + void __iomem *mask_base; + struct pci_dev *dev; + +#ifdef CONFIG_PM + /* PM save area for MSIX address/data */ + struct msi_msg msg_save; +#endif +}; + +/* + * The arch hook for setup up msi irqs + */ +int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev); +void arch_teardown_msi_irq(unsigned int irq); + + +#endif /* LINUX_MSI_H */ diff --git a/include/linux/nfsd/const.h b/include/linux/nfsd/const.h index b75bb1b38d..f0cc777905 100644 --- a/include/linux/nfsd/const.h +++ b/include/linux/nfsd/const.h @@ -20,17 +20,31 @@ #define NFSSVC_MAXVERS 3 /* - * Maximum blocksize supported by daemon currently at 32K + * Maximum blocksizes supported by daemon under various circumstances. */ -#define NFSSVC_MAXBLKSIZE (32*1024) +#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD +/* NFSv2 is limited by the protocol specification, see RFC 1094 */ +#define NFSSVC_MAXBLKSIZE_V2 (8*1024) #ifdef __KERNEL__ +#include + #ifndef NFS_SUPER_MAGIC # define NFS_SUPER_MAGIC 0x6969 #endif -#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE) +/* + * Largest number of bytes we need to allocate for an NFS + * call or reply. Used to control buffer sizes. We use + * the length of v3 WRITE, READDIR and READDIR replies + * which are an RPC header, up to 26 XDR units of reply + * data, and some page data. + * + * Note that accuracy here doesn't matter too much as the + * size is rounded up to a page size when allocating space. + */ +#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE) #ifdef CONFIG_NFSD_V4 # define NFSSVC_XDRSIZE NFS4_SVC_XDRSIZE diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index d2a8abb501..6e78ea969f 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -45,15 +45,36 @@ #ifdef __KERNEL__ +/* + * FS Locations + */ + +#define MAX_FS_LOCATIONS 128 + +struct nfsd4_fs_location { + char *hosts; /* colon separated list of hosts */ + char *path; /* slash separated list of path components */ +}; + +struct nfsd4_fs_locations { + uint32_t locations_count; + struct nfsd4_fs_location *locations; +/* If we're not actually serving this data ourselves (only providing a + * list of replicas that do serve it) then we set "migrated": */ + int migrated; +}; + struct svc_export { struct cache_head h; struct auth_domain * ex_client; int ex_flags; struct vfsmount * ex_mnt; struct dentry * ex_dentry; + char * ex_path; uid_t ex_anon_uid; gid_t ex_anon_gid; int ex_fsid; + struct nfsd4_fs_locations ex_fslocs; }; /* an "export key" (expkey) maps a filehandlefragement to an diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index e1dbc86c27..d0d4aae708 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -145,6 +145,7 @@ int nfsd_vers(int vers, enum vers_op change); void nfsd_reset_versions(void); int nfsd_create_serv(void); +extern int nfsd_max_blksize; /* * NFSv4 State @@ -215,6 +216,7 @@ void nfsd_lockd_shutdown(void); #define nfserr_clid_inuse __constant_htonl(NFSERR_CLID_INUSE) #define nfserr_stale_clientid __constant_htonl(NFSERR_STALE_CLIENTID) #define nfserr_resource __constant_htonl(NFSERR_RESOURCE) +#define nfserr_moved __constant_htonl(NFSERR_MOVED) #define nfserr_nofilehandle __constant_htonl(NFSERR_NOFILEHANDLE) #define nfserr_minor_vers_mismatch __constant_htonl(NFSERR_MINOR_VERS_MISMATCH) #define nfserr_share_denied __constant_htonl(NFSERR_SHARE_DENIED) @@ -291,7 +293,6 @@ static inline int is_fsid(struct svc_fh *fh, struct knfsd_fh *reffh) /* * The following attributes are currently not supported by the NFSv4 server: * ARCHIVE (deprecated anyway) - * FS_LOCATIONS (will be supported eventually) * HIDDEN (unlikely to be supported any time soon) * MIMETYPE (unlikely to be supported any time soon) * QUOTA_* (will be supported in a forthcoming patch) @@ -307,7 +308,7 @@ static inline int is_fsid(struct svc_fh *fh, struct knfsd_fh *reffh) | FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | FATTR4_WORD0_CASE_INSENSITIVE \ | FATTR4_WORD0_CASE_PRESERVING | FATTR4_WORD0_CHOWN_RESTRICTED \ | FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FILEID | FATTR4_WORD0_FILES_AVAIL \ - | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_HOMOGENEOUS \ + | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_HOMOGENEOUS \ | FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \ | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL) diff --git a/include/linux/nfsd/xdr.h b/include/linux/nfsd/xdr.h index a38f9d776d..0e53de87d8 100644 --- a/include/linux/nfsd/xdr.h +++ b/include/linux/nfsd/xdr.h @@ -30,7 +30,6 @@ struct nfsd_readargs { struct svc_fh fh; __u32 offset; __u32 count; - struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; @@ -38,7 +37,6 @@ struct nfsd_writeargs { svc_fh fh; __u32 offset; int len; - struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; diff --git a/include/linux/nfsd/xdr3.h b/include/linux/nfsd/xdr3.h index a4322741f8..474d882dc2 100644 --- a/include/linux/nfsd/xdr3.h +++ b/include/linux/nfsd/xdr3.h @@ -33,7 +33,6 @@ struct nfsd3_readargs { struct svc_fh fh; __u64 offset; __u32 count; - struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; @@ -43,7 +42,6 @@ struct nfsd3_writeargs { __u32 count; int stable; __u32 len; - struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 77adba7d22..66e642762a 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -241,7 +241,6 @@ struct nfsd4_read { stateid_t rd_stateid; /* request */ u64 rd_offset; /* request */ u32 rd_length; /* request */ - struct kvec rd_iov[RPCSVC_MAXPAGES]; int rd_vlen; struct file *rd_filp; @@ -326,7 +325,6 @@ struct nfsd4_write { u64 wr_offset; /* request */ u32 wr_stable_how; /* request */ u32 wr_buflen; /* request */ - struct kvec wr_vec[RPCSVC_MAXPAGES]; /* request */ int wr_vlen; u32 wr_bytes_written; /* response */ diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 7ff386a6ae..10a43ed052 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -12,9 +12,10 @@ #include #include #include +#include /* - * Notifier chains are of three types: + * Notifier chains are of four types: * * Atomic notifier chains: Chain callbacks run in interrupt/atomic * context. Callouts are not allowed to block. @@ -23,13 +24,27 @@ * Raw notifier chains: There are no restrictions on callbacks, * registration, or unregistration. All locking and protection * must be provided by the caller. + * SRCU notifier chains: A variant of blocking notifier chains, with + * the same restrictions. * * atomic_notifier_chain_register() may be called from an atomic context, - * but blocking_notifier_chain_register() must be called from a process - * context. Ditto for the corresponding _unregister() routines. + * but blocking_notifier_chain_register() and srcu_notifier_chain_register() + * must be called from a process context. Ditto for the corresponding + * _unregister() routines. * - * atomic_notifier_chain_unregister() and blocking_notifier_chain_unregister() - * _must not_ be called from within the call chain. + * atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(), + * and srcu_notifier_chain_unregister() _must not_ be called from within + * the call chain. + * + * SRCU notifier chains are an alternative form of blocking notifier chains. + * They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for + * protection of the chain links. This means there is _very_ low overhead + * in srcu_notifier_call_chain(): no cache bounces and no memory barriers. + * As compensation, srcu_notifier_chain_unregister() is rather expensive. + * SRCU notifier chains should be used when the chain will be called very + * often but notifier_blocks will seldom be removed. Also, SRCU notifier + * chains are slightly more difficult to use because they require special + * runtime initialization. */ struct notifier_block { @@ -52,6 +67,12 @@ struct raw_notifier_head { struct notifier_block *head; }; +struct srcu_notifier_head { + struct mutex mutex; + struct srcu_struct srcu; + struct notifier_block *head; +}; + #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ spin_lock_init(&(name)->lock); \ (name)->head = NULL; \ @@ -64,6 +85,11 @@ struct raw_notifier_head { (name)->head = NULL; \ } while (0) +/* srcu_notifier_heads must be initialized and cleaned up dynamically */ +extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); +#define srcu_cleanup_notifier_head(name) \ + cleanup_srcu_struct(&(name)->srcu); + #define ATOMIC_NOTIFIER_INIT(name) { \ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .head = NULL } @@ -72,6 +98,7 @@ struct raw_notifier_head { .head = NULL } #define RAW_NOTIFIER_INIT(name) { \ .head = NULL } +/* srcu_notifier_heads cannot be initialized statically */ #define ATOMIC_NOTIFIER_HEAD(name) \ struct atomic_notifier_head name = \ @@ -91,6 +118,8 @@ extern int blocking_notifier_chain_register(struct blocking_notifier_head *, struct notifier_block *); extern int raw_notifier_chain_register(struct raw_notifier_head *, struct notifier_block *); +extern int srcu_notifier_chain_register(struct srcu_notifier_head *, + struct notifier_block *); extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, struct notifier_block *); @@ -98,6 +127,8 @@ extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, struct notifier_block *); extern int raw_notifier_chain_unregister(struct raw_notifier_head *, struct notifier_block *); +extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *, + struct notifier_block *); extern int atomic_notifier_call_chain(struct atomic_notifier_head *, unsigned long val, void *v); @@ -105,6 +136,8 @@ extern int blocking_notifier_call_chain(struct blocking_notifier_head *, unsigned long val, void *v); extern int raw_notifier_call_chain(struct raw_notifier_head *, unsigned long val, void *v); +extern int srcu_notifier_call_chain(struct srcu_notifier_head *, + unsigned long val, void *v); #define NOTIFY_DONE 0x0000 /* Don't care */ #define NOTIFY_OK 0x0001 /* Suits me */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 4431ce4e1e..5c604f5fad 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -595,6 +595,7 @@ struct msix_entry { u16 entry; /* driver uses to specify entry, OS writes */ }; + #ifndef CONFIG_PCI_MSI static inline void pci_scan_msi_device(struct pci_dev *dev) {} static inline int pci_enable_msi(struct pci_dev *dev) {return -1;} @@ -613,6 +614,12 @@ extern void pci_disable_msix(struct pci_dev *dev); extern void msi_remove_pci_irq_vectors(struct pci_dev *dev); #endif +#ifdef CONFIG_HT_IRQ +/* The functions a driver should call */ +int ht_create_irq(struct pci_dev *dev, int idx); +void ht_destroy_irq(unsigned int irq); +#endif /* CONFIG_HT_IRQ */ + extern void pci_block_user_cfg_access(struct pci_dev *dev); extern void pci_unblock_user_cfg_access(struct pci_dev *dev); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 7d0e26cba4..c312a12ad2 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -12,6 +12,11 @@ * PCI Local Bus Specification * PCI to PCI Bridge Specification * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification */ #ifndef LINUX_PCI_REGS_H @@ -463,4 +468,20 @@ #define PCI_PWR_CAP 12 /* Capability */ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ +/* Hypertransport sub capability types */ +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + + #endif /* LINUX_PCI_REGS_H */ diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index b4ca73d658..c6b7485eac 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -19,7 +19,7 @@ * * Author: Dipankar Sarma * - * Based on the original work by Paul McKenney + * Based on the original work by Paul McKenney * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. * Papers: * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf @@ -66,6 +66,8 @@ struct rcu_ctrlblk { long completed; /* Number of the last completed batch */ int next_pending; /* Is the next batch already waiting? */ + int signaled; + spinlock_t lock ____cacheline_internodealigned_in_smp; cpumask_t cpumask; /* CPUs that need to switch in order */ /* for current batch to proceed. */ @@ -106,9 +108,6 @@ struct rcu_data { long blimit; /* Upper limit on a processed batch */ int cpu; struct rcu_head barrier; -#ifdef CONFIG_SMP - long last_rs_qlen; /* qlen during the last resched */ -#endif }; DECLARE_PER_CPU(struct rcu_data, rcu_data); diff --git a/include/linux/scx200.h b/include/linux/scx200.h index 693c0557e7..de466e11e2 100644 --- a/include/linux/scx200.h +++ b/include/linux/scx200.h @@ -32,7 +32,7 @@ extern unsigned scx200_cb_base; /* High Resolution Timer */ #define SCx200_TIMER_OFFSET 0x08 -#define SCx200_TIMER_SIZE 0x05 +#define SCx200_TIMER_SIZE 0x06 /* Clock Generators */ #define SCx200_CLOCKGEN_OFFSET 0x10 diff --git a/include/linux/slab.h b/include/linux/slab.h index 70be57d8ae..c4947b8a2c 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -77,13 +77,6 @@ struct cache_sizes { extern struct cache_sizes malloc_sizes[]; extern void *__kmalloc(size_t, gfp_t); -#ifndef CONFIG_DEBUG_SLAB -#define ____kmalloc(size, flags) __kmalloc(size, flags) -#else -extern void *__kmalloc_track_caller(size_t, gfp_t, void*); -#define ____kmalloc(size, flags) \ - __kmalloc_track_caller(size, flags, __builtin_return_address(0)) -#endif /** * kmalloc - allocate memory @@ -153,6 +146,23 @@ found: return __kmalloc(size, flags); } +/* + * kmalloc_track_caller is a special version of kmalloc that records the + * calling function of the routine calling it for slab leak tracking instead + * of just the calling function (confusing, eh?). + * It's useful when the call to kmalloc comes from a widely-used standard + * allocator where we care about the real place the memory allocation + * request comes from. + */ +#ifndef CONFIG_DEBUG_SLAB +#define kmalloc_track_caller(size, flags) \ + __kmalloc(size, flags) +#else +extern void *__kmalloc_track_caller(size_t, gfp_t, void*); +#define kmalloc_track_caller(size, flags) \ + __kmalloc_track_caller(size, flags, __builtin_return_address(0)) +#endif + extern void *__kzalloc(size_t, gfp_t); /** @@ -271,7 +281,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) #define kmem_cache_alloc_node(c, f, n) kmem_cache_alloc(c, f) #define kmalloc_node(s, f, n) kmalloc(s, f) #define kzalloc(s, f) __kzalloc(s, f) -#define ____kmalloc kmalloc +#define kmalloc_track_caller kmalloc #endif /* CONFIG_SLOB */ diff --git a/include/linux/sound.h b/include/linux/sound.h index f63d8342ff..9e2a94feed 100644 --- a/include/linux/sound.h +++ b/include/linux/sound.h @@ -35,10 +35,8 @@ extern int register_sound_special_device(const struct file_operations *fops, int extern int register_sound_mixer(const struct file_operations *fops, int dev); extern int register_sound_midi(const struct file_operations *fops, int dev); extern int register_sound_dsp(const struct file_operations *fops, int dev); -extern int register_sound_synth(const struct file_operations *fops, int dev); extern void unregister_sound_special(int unit); extern void unregister_sound_mixer(int unit); extern void unregister_sound_midi(int unit); extern void unregister_sound_dsp(int unit); -extern void unregister_sound_synth(int unit); diff --git a/include/linux/srcu.h b/include/linux/srcu.h new file mode 100644 index 0000000000..aca0eee539 --- /dev/null +++ b/include/linux/srcu.h @@ -0,0 +1,53 @@ +/* + * Sleepable Read-Copy Update mechanism for mutual exclusion + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Paul McKenney + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU/ *.txt + * + */ + +#ifndef _LINUX_SRCU_H +#define _LINUX_SRCU_H + +struct srcu_struct_array { + int c[2]; +}; + +struct srcu_struct { + int completed; + struct srcu_struct_array *per_cpu_ref; + struct mutex mutex; +}; + +#ifndef CONFIG_PREEMPT +#define srcu_barrier() barrier() +#else /* #ifndef CONFIG_PREEMPT */ +#define srcu_barrier() +#endif /* #else #ifndef CONFIG_PREEMPT */ + +int init_srcu_struct(struct srcu_struct *sp); +void cleanup_srcu_struct(struct srcu_struct *sp); +int srcu_read_lock(struct srcu_struct *sp) __acquires(sp); +void srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp); +void synchronize_srcu(struct srcu_struct *sp); +long srcu_batches_completed(struct srcu_struct *sp); + +#endif diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 862c0d8c83..534cdc7be5 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -20,9 +20,6 @@ /* size of the nodename buffer */ #define UNX_MAXNODENAME 32 -/* Maximum size (in bytes) of an rpc credential or verifier */ -#define RPC_MAX_AUTH_SIZE (400) - /* Work around the lack of a VFS credential */ struct auth_cred { uid_t uid; diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index b5612c958c..3699dff7db 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -163,6 +163,17 @@ static inline void cache_put(struct cache_head *h, struct cache_detail *cd) kref_put(&h->ref, cd->cache_put); } +static inline int cache_valid(struct cache_head *h) +{ + /* If an item has been unhashed pending removal when + * the refcount drops to 0, the expiry_time will be + * set to 0. We don't want to consider such items + * valid in this context even though CACHE_VALID is + * set. + */ + return (h->expiry_time != 0 && test_bit(CACHE_VALID, &h->flags)); +} + extern int cache_check(struct cache_detail *detail, struct cache_head *h, struct cache_req *rqstp); extern void cache_flush(void); diff --git a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h index 8d10d14883..1e65f2dd80 100644 --- a/include/linux/sunrpc/msg_prot.h +++ b/include/linux/sunrpc/msg_prot.h @@ -11,6 +11,9 @@ #define RPC_VERSION 2 +/* size of an XDR encoding unit in bytes, i.e. 32bit */ +#define XDR_UNIT (4) + /* spec defines authentication flavor as an unsigned 32 bit integer */ typedef u32 rpc_authflavor_t; @@ -34,6 +37,9 @@ enum rpc_auth_flavors { RPC_AUTH_GSS_SPKMP = 390011, }; +/* Maximum size (in bytes) of an rpc credential or verifier */ +#define RPC_MAX_AUTH_SIZE (400) + enum rpc_msg_type { RPC_CALL = 0, RPC_REPLY = 1 @@ -101,5 +107,39 @@ typedef __be32 rpc_fraghdr; #define RPC_FRAGMENT_SIZE_MASK (~RPC_LAST_STREAM_FRAGMENT) #define RPC_MAX_FRAGMENT_SIZE ((1U << 31) - 1) +/* + * RPC call and reply header size as number of 32bit words (verifier + * size computed separately, see below) + */ +#define RPC_CALLHDRSIZE (6) +#define RPC_REPHDRSIZE (4) + + +/* + * Maximum RPC header size, including authentication, + * as number of 32bit words (see RFCs 1831, 1832). + * + * xid 1 xdr unit = 4 bytes + * mtype 1 + * rpc_version 1 + * program 1 + * prog_version 1 + * procedure 1 + * cred { + * flavor 1 + * length 1 + * body 100 xdr units = 400 bytes + * } + * verf { + * flavor 1 + * length 1 + * body 100 xdr units = 400 bytes + * } + * TOTAL 210 xdr units = 840 bytes + */ +#define RPC_MAX_HEADER_WITH_AUTH \ + (RPC_CALLHDRSIZE + 2*(2+RPC_MAX_AUTH_SIZE/4)) + + #endif /* __KERNEL__ */ #endif /* _LINUX_SUNRPC_MSGPROT_H_ */ diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 4ebcdf91f3..d6288e89fd 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -95,8 +96,28 @@ static inline void svc_get(struct svc_serv *serv) * Maximum payload size supported by a kernel RPC server. * This is use to determine the max number of pages nfsd is * willing to return in a single READ operation. + * + * These happen to all be powers of 2, which is not strictly + * necessary but helps enforce the real limitation, which is + * that they should be multiples of PAGE_CACHE_SIZE. + * + * For UDP transports, a block plus NFS,RPC, and UDP headers + * has to fit into the IP datagram limit of 64K. The largest + * feasible number for all known page sizes is probably 48K, + * but we choose 32K here. This is the same as the historical + * Linux limit; someone who cares more about NFS/UDP performance + * can test a larger number. + * + * For TCP transports we have more freedom. A size of 1MB is + * chosen to match the client limit. Other OSes are known to + * have larger limits, but those numbers are probably beyond + * the point of diminishing returns. */ -#define RPCSVC_MAXPAYLOAD (64*1024u) +#define RPCSVC_MAXPAYLOAD (1*1024*1024u) +#define RPCSVC_MAXPAYLOAD_TCP RPCSVC_MAXPAYLOAD +#define RPCSVC_MAXPAYLOAD_UDP (32*1024u) + +extern u32 svc_max_payload(const struct svc_rqst *rqstp); /* * RPC Requsts and replies are stored in one or more pages. @@ -170,7 +191,6 @@ static inline void svc_putu32(struct kvec *iov, __be32 val) /* * The context of a single thread, including the request currently being * processed. - * NOTE: First two items must be prev/next. */ struct svc_rqst { struct list_head rq_list; /* idle list */ @@ -189,12 +209,11 @@ struct svc_rqst { struct xdr_buf rq_arg; struct xdr_buf rq_res; - struct page * rq_argpages[RPCSVC_MAXPAGES]; - struct page * rq_respages[RPCSVC_MAXPAGES]; - int rq_restailpage; - short rq_argused; /* pages used for argument */ - short rq_arghi; /* pages available in argument page list */ - short rq_resused; /* pages used for result */ + struct page * rq_pages[RPCSVC_MAXPAGES]; + struct page * *rq_respages; /* points into rq_pages */ + int rq_resused; /* number of pages used for result */ + + struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ __be32 rq_xid; /* transmission id */ u32 rq_prog; /* program number */ @@ -255,63 +274,18 @@ xdr_ressize_check(struct svc_rqst *rqstp, __be32 *p) return vec->iov_len <= PAGE_SIZE; } -static inline struct page * -svc_take_res_page(struct svc_rqst *rqstp) -{ - if (rqstp->rq_arghi <= rqstp->rq_argused) - return NULL; - rqstp->rq_arghi--; - rqstp->rq_respages[rqstp->rq_resused] = - rqstp->rq_argpages[rqstp->rq_arghi]; - return rqstp->rq_respages[rqstp->rq_resused++]; -} - -static inline void svc_take_page(struct svc_rqst *rqstp) -{ - if (rqstp->rq_arghi <= rqstp->rq_argused) { - WARN_ON(1); - return; - } - rqstp->rq_arghi--; - rqstp->rq_respages[rqstp->rq_resused] = - rqstp->rq_argpages[rqstp->rq_arghi]; - rqstp->rq_resused++; -} - -static inline void svc_pushback_allpages(struct svc_rqst *rqstp) -{ - while (rqstp->rq_resused) { - if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) - continue; - rqstp->rq_argpages[rqstp->rq_arghi++] = - rqstp->rq_respages[rqstp->rq_resused]; - rqstp->rq_respages[rqstp->rq_resused] = NULL; - } -} - -static inline void svc_pushback_unused_pages(struct svc_rqst *rqstp) +static inline void svc_free_res_pages(struct svc_rqst *rqstp) { - while (rqstp->rq_resused && - rqstp->rq_res.pages != &rqstp->rq_respages[rqstp->rq_resused]) { - - if (rqstp->rq_respages[--rqstp->rq_resused] != NULL) { - rqstp->rq_argpages[rqstp->rq_arghi++] = - rqstp->rq_respages[rqstp->rq_resused]; - rqstp->rq_respages[rqstp->rq_resused] = NULL; + while (rqstp->rq_resused) { + struct page **pp = (rqstp->rq_respages + + --rqstp->rq_resused); + if (*pp) { + put_page(*pp); + *pp = NULL; } } } -static inline void svc_free_allpages(struct svc_rqst *rqstp) -{ - while (rqstp->rq_resused) { - if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) - continue; - put_page(rqstp->rq_respages[rqstp->rq_resused]); - rqstp->rq_respages[rqstp->rq_resused] = NULL; - } -} - struct svc_deferred_req { u32 prot; /* protocol (UDP or TCP) */ struct sockaddr_in addr; @@ -347,6 +321,9 @@ struct svc_version { struct svc_procedure * vs_proc; /* per-procedure info */ u32 vs_xdrsize; /* xdrsize needed for this version */ + unsigned int vs_hidden : 1; /* Don't register with portmapper. + * Only used for nfsacl so far. */ + /* Override dispatch function (e.g. when caching replies). * A return value of 0 means drop the request. * vs_dispatch == NULL means use default dispatcher. diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index a6601650de..de92619b08 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -126,6 +126,7 @@ extern struct auth_domain *auth_domain_find(char *name); extern struct auth_domain *auth_unix_lookup(struct in_addr addr); extern int auth_unix_forget_old(struct auth_domain *dom); extern void svcauth_unix_purge(void); +extern void svcauth_unix_info_release(void *); static inline unsigned long hash_str(char *name, int bits) { diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 4c296152cb..98b21ad370 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -54,6 +54,9 @@ struct svc_sock { int sk_reclen; /* length of record */ int sk_tcplen; /* current read length */ time_t sk_lastrecv; /* time of last received request */ + + /* cache of various info for TCP sockets */ + void *sk_info_authunix; }; /* diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 6cf6265807..60394fbc4c 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -15,6 +15,7 @@ #include #include #include +#include extern unsigned int xprt_udp_slot_table_entries; extern unsigned int xprt_tcp_slot_table_entries; @@ -23,13 +24,6 @@ extern unsigned int xprt_tcp_slot_table_entries; #define RPC_DEF_SLOT_TABLE (16U) #define RPC_MAX_SLOT_TABLE (128U) -/* - * RPC call and reply header size as number of 32bit words (verifier - * size computed separately) - */ -#define RPC_CALLHDRSIZE 6 -#define RPC_REPHDRSIZE 4 - /* * Parameters for choosing a free port */ diff --git a/include/linux/tifm.h b/include/linux/tifm.h new file mode 100644 index 0000000000..203dd5e11e --- /dev/null +++ b/include/linux/tifm.h @@ -0,0 +1,158 @@ +/* + * tifm.h - TI FlashMedia driver + * + * Copyright (C) 2006 Alex Dubov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _TIFM_H +#define _TIFM_H + +#include +#include +#include +#include +#include + +/* Host registers (relative to pci base address): */ +enum { + FM_SET_INTERRUPT_ENABLE = 0x008, + FM_CLEAR_INTERRUPT_ENABLE = 0x00c, + FM_INTERRUPT_STATUS = 0x014 }; + +/* Socket registers (relative to socket base address): */ +enum { + SOCK_CONTROL = 0x004, + SOCK_PRESENT_STATE = 0x008, + SOCK_DMA_ADDRESS = 0x00c, + SOCK_DMA_CONTROL = 0x010, + SOCK_DMA_FIFO_INT_ENABLE_SET = 0x014, + SOCK_DMA_FIFO_INT_ENABLE_CLEAR = 0x018, + SOCK_DMA_FIFO_STATUS = 0x020, + SOCK_FIFO_CONTROL = 0x024, + SOCK_FIFO_PAGE_SIZE = 0x028, + SOCK_MMCSD_COMMAND = 0x104, + SOCK_MMCSD_ARG_LOW = 0x108, + SOCK_MMCSD_ARG_HIGH = 0x10c, + SOCK_MMCSD_CONFIG = 0x110, + SOCK_MMCSD_STATUS = 0x114, + SOCK_MMCSD_INT_ENABLE = 0x118, + SOCK_MMCSD_COMMAND_TO = 0x11c, + SOCK_MMCSD_DATA_TO = 0x120, + SOCK_MMCSD_DATA = 0x124, + SOCK_MMCSD_BLOCK_LEN = 0x128, + SOCK_MMCSD_NUM_BLOCKS = 0x12c, + SOCK_MMCSD_BUFFER_CONFIG = 0x130, + SOCK_MMCSD_SPI_CONFIG = 0x134, + SOCK_MMCSD_SDIO_MODE_CONFIG = 0x138, + SOCK_MMCSD_RESPONSE = 0x144, + SOCK_MMCSD_SDIO_SR = 0x164, + SOCK_MMCSD_SYSTEM_CONTROL = 0x168, + SOCK_MMCSD_SYSTEM_STATUS = 0x16c, + SOCK_MS_COMMAND = 0x184, + SOCK_MS_DATA = 0x188, + SOCK_MS_STATUS = 0x18c, + SOCK_MS_SYSTEM = 0x190, + SOCK_FIFO_ACCESS = 0x200 }; + + +#define TIFM_IRQ_ENABLE 0x80000000 +#define TIFM_IRQ_SOCKMASK 0x00000001 +#define TIFM_IRQ_CARDMASK 0x00000100 +#define TIFM_IRQ_FIFOMASK 0x00010000 +#define TIFM_IRQ_SETALL 0xffffffff +#define TIFM_IRQ_SETALLSOCK 0x0000000f + +#define TIFM_CTRL_LED 0x00000040 +#define TIFM_CTRL_FAST_CLK 0x00000100 + +#define TIFM_SOCK_STATE_OCCUPIED 0x00000008 +#define TIFM_SOCK_STATE_POWERED 0x00000080 + +#define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */ +#define TIFM_FIFO_INT_SETALL 0x0000ffff +#define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */ + +#define TIFM_DMA_RESET 0x00000002 /* Meaning of this constant is unverified */ +#define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ +#define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ + +typedef enum {FM_NULL = 0, FM_XD = 0x01, FM_MS = 0x02, FM_SD = 0x03} tifm_media_id; + +struct tifm_driver; +struct tifm_dev { + char __iomem *addr; + spinlock_t lock; + tifm_media_id media_id; + char wq_name[KOBJ_NAME_LEN]; + struct workqueue_struct *wq; + + unsigned int (*signal_irq)(struct tifm_dev *sock, + unsigned int sock_irq_status); + + struct tifm_driver *drv; + struct device dev; +}; + +struct tifm_driver { + tifm_media_id *id_table; + int (*probe)(struct tifm_dev *dev); + void (*remove)(struct tifm_dev *dev); + + struct device_driver driver; +}; + +struct tifm_adapter { + char __iomem *addr; + unsigned int irq_status; + unsigned int insert_mask; + unsigned int remove_mask; + spinlock_t lock; + unsigned int id; + unsigned int max_sockets; + char wq_name[KOBJ_NAME_LEN]; + unsigned int inhibit_new_cards; + struct workqueue_struct *wq; + struct work_struct media_inserter; + struct work_struct media_remover; + struct tifm_dev **sockets; + struct class_device cdev; + struct device *dev; + + void (*eject)(struct tifm_adapter *fm, struct tifm_dev *sock); +}; + +struct tifm_adapter *tifm_alloc_adapter(void); +void tifm_free_device(struct device *dev); +void tifm_free_adapter(struct tifm_adapter *fm); +int tifm_add_adapter(struct tifm_adapter *fm); +void tifm_remove_adapter(struct tifm_adapter *fm); +struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id); +int tifm_register_driver(struct tifm_driver *drv); +void tifm_unregister_driver(struct tifm_driver *drv); +void tifm_eject(struct tifm_dev *sock); +int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, + int direction); +void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, + int direction); + + +static inline void *tifm_get_drvdata(struct tifm_dev *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +struct tifm_device_id { + tifm_media_id media_id; +}; + +#endif diff --git a/include/linux/utsname.h b/include/linux/utsname.h index 02e4b69720..a4555fe375 100644 --- a/include/linux/utsname.h +++ b/include/linux/utsname.h @@ -1,11 +1,6 @@ #ifndef _LINUX_UTSNAME_H #define _LINUX_UTSNAME_H -#include -#include -#include -#include - #define __OLD_UTS_LEN 8 struct oldold_utsname { @@ -35,6 +30,13 @@ struct new_utsname { char domainname[65]; }; +#ifdef __KERNEL__ + +#include +#include +#include +#include + struct uts_namespace { struct kref kref; struct new_utsname name; @@ -86,4 +88,7 @@ static inline struct new_utsname *init_utsname(void) } extern struct rw_semaphore uts_sem; -#endif + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_UTSNAME_H */ diff --git a/include/linux/wavefront.h b/include/linux/wavefront.h deleted file mode 100644 index 51ab3c933a..0000000000 --- a/include/linux/wavefront.h +++ /dev/null @@ -1,675 +0,0 @@ -#ifndef __wavefront_h__ -#define __wavefront_h__ - -/* WaveFront header file. - * - * Copyright (C) by Paul Barton-Davis 1998 - * - * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#if (!defined(__GNUC__) && !defined(__GNUG__)) - - You will not be able to compile this file correctly without gcc, because - it is necessary to pack the "wavefront_alias" structure to a size - of 22 bytes, corresponding to 16-bit alignment (as would have been - the case on the original platform, MS-DOS). If this is not done, - then WavePatch-format files cannot be read/written correctly. - The method used to do this here ("__attribute__((packed)") is - completely compiler dependent. - - All other wavefront_* types end up aligned to 32 bit values and - still have the same (correct) size. - -#else - - /* However, note that as of G++ 2.7.3.2, g++ was unable to - correctly parse *type* __attribute__ tags. It will do the - right thing if we use the "packed" attribute on each struct - member, which has the same semantics anyway. - */ - -#endif /* __GNUC__ */ - -/***************************** WARNING ******************************** - PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO - BE USED WITH EITHER C *OR* C++. - **********************************************************************/ - -#ifndef NUM_MIDIKEYS -#define NUM_MIDIKEYS 128 -#endif /* NUM_MIDIKEYS */ - -#ifndef NUM_MIDICHANNELS -#define NUM_MIDICHANNELS 16 -#endif /* NUM_MIDICHANNELS */ - -/* These are very useful/important. the original wavefront interface - was developed on a 16 bit system, where sizeof(int) = 2 - bytes. Defining things like this makes the code much more portable, and - easier to understand without having to toggle back and forth - between a 16-bit view of the world and a 32-bit one. - */ - -typedef short INT16; -typedef unsigned short UINT16; -typedef int INT32; -typedef unsigned int UINT32; -typedef char CHAR8; -typedef unsigned char UCHAR8; - -/* Pseudo-commands not part of the WaveFront command set. - These are used for various driver controls and direct - hardware control. - */ - -#define WFC_DEBUG_DRIVER 0 -#define WFC_FX_IOCTL 1 -#define WFC_PATCH_STATUS 2 -#define WFC_PROGRAM_STATUS 3 -#define WFC_SAMPLE_STATUS 4 -#define WFC_DISABLE_INTERRUPTS 5 -#define WFC_ENABLE_INTERRUPTS 6 -#define WFC_INTERRUPT_STATUS 7 -#define WFC_ROMSAMPLES_RDONLY 8 -#define WFC_IDENTIFY_SLOT_TYPE 9 - -/* Wavefront synth commands - */ - -#define WFC_DOWNLOAD_SAMPLE 0x80 -#define WFC_DOWNLOAD_BLOCK 0x81 -#define WFC_DOWNLOAD_MULTISAMPLE 0x82 -#define WFC_DOWNLOAD_SAMPLE_ALIAS 0x83 -#define WFC_DELETE_SAMPLE 0x84 -#define WFC_REPORT_FREE_MEMORY 0x85 -#define WFC_DOWNLOAD_PATCH 0x86 -#define WFC_DOWNLOAD_PROGRAM 0x87 -#define WFC_SET_SYNTHVOL 0x89 -#define WFC_SET_NVOICES 0x8B -#define WFC_DOWNLOAD_DRUM 0x90 -#define WFC_GET_SYNTHVOL 0x92 -#define WFC_GET_NVOICES 0x94 -#define WFC_DISABLE_CHANNEL 0x9A -#define WFC_ENABLE_CHANNEL 0x9B -#define WFC_MISYNTH_OFF 0x9D -#define WFC_MISYNTH_ON 0x9E -#define WFC_FIRMWARE_VERSION 0x9F -#define WFC_GET_NSAMPLES 0xA0 -#define WFC_DISABLE_DRUM_PROGRAM 0xA2 -#define WFC_UPLOAD_PATCH 0xA3 -#define WFC_UPLOAD_PROGRAM 0xA4 -#define WFC_SET_TUNING 0xA6 -#define WFC_GET_TUNING 0xA7 -#define WFC_VMIDI_ON 0xA8 -#define WFC_VMIDI_OFF 0xA9 -#define WFC_MIDI_STATUS 0xAA -#define WFC_GET_CHANNEL_STATUS 0xAB -#define WFC_DOWNLOAD_SAMPLE_HEADER 0xAC -#define WFC_UPLOAD_SAMPLE_HEADER 0xAD -#define WFC_UPLOAD_MULTISAMPLE 0xAE -#define WFC_UPLOAD_SAMPLE_ALIAS 0xAF -#define WFC_IDENTIFY_SAMPLE_TYPE 0xB0 -#define WFC_DOWNLOAD_EDRUM_PROGRAM 0xB1 -#define WFC_UPLOAD_EDRUM_PROGRAM 0xB2 -#define WFC_SET_EDRUM_CHANNEL 0xB3 -#define WFC_INSTOUT_LEVELS 0xB4 -#define WFC_PEAKOUT_LEVELS 0xB5 -#define WFC_REPORT_CHANNEL_PROGRAMS 0xB6 -#define WFC_HARDWARE_VERSION 0xCF -#define WFC_UPLOAD_SAMPLE_PARAMS 0xD7 -#define WFC_DOWNLOAD_OS 0xF1 -#define WFC_NOOP 0xFF - -#define WF_MAX_SAMPLE 512 -#define WF_MAX_PATCH 256 -#define WF_MAX_PROGRAM 128 - -#define WF_SECTION_MAX 44 /* longest OS section length */ - -/* # of bytes we send to the board when sending it various kinds of - substantive data, such as samples, patches and programs. -*/ - -#define WF_PROGRAM_BYTES 32 -#define WF_PATCH_BYTES 132 -#define WF_SAMPLE_BYTES 27 -#define WF_SAMPLE_HDR_BYTES 25 -#define WF_ALIAS_BYTES 25 -#define WF_DRUM_BYTES 9 -#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */ - -#define WF_ACK 0x80 -#define WF_DMA_ACK 0x81 - -/* OR-values for MIDI status bits */ - -#define WF_MIDI_VIRTUAL_ENABLED 0x1 -#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2 -#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4 - -/* slot indexes for struct address_info: makes code a little more mnemonic */ - -#define WF_SYNTH_SLOT 0 -#define WF_INTERNAL_MIDI_SLOT 1 -#define WF_EXTERNAL_MIDI_SLOT 2 - -/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401 - emulation. Note these NEVER show up in output from the device and - should NEVER be used in input unless Virtual MIDI mode has been - disabled. If they do show up as input, the results are unpredictable. -*/ - -#define WF_EXTERNAL_SWITCH 0xFD -#define WF_INTERNAL_SWITCH 0xF9 - -/* Debugging flags */ - -#define WF_DEBUG_CMD 0x1 -#define WF_DEBUG_DATA 0x2 -#define WF_DEBUG_LOAD_PATCH 0x4 -#define WF_DEBUG_IO 0x8 - -/* WavePatch file format stuff */ - -#define WF_WAVEPATCH_VERSION 120; /* Current version number (1.2) */ -#define WF_MAX_COMMENT 64 /* Comment length */ -#define WF_NUM_LAYERS 4 -#define WF_NAME_LENGTH 32 -#define WF_SOURCE_LENGTH 260 - -#define BankFileID "Bank" -#define DrumkitFileID "DrumKit" -#define ProgramFileID "Program" - -struct wf_envelope -{ - UCHAR8 attack_time:7; - UCHAR8 Unused1:1; - - UCHAR8 decay1_time:7; - UCHAR8 Unused2:1; - - UCHAR8 decay2_time:7; - UCHAR8 Unused3:1; - - UCHAR8 sustain_time:7; - UCHAR8 Unused4:1; - - UCHAR8 release_time:7; - UCHAR8 Unused5:1; - - UCHAR8 release2_time:7; - UCHAR8 Unused6:1; - - CHAR8 attack_level; - CHAR8 decay1_level; - CHAR8 decay2_level; - CHAR8 sustain_level; - CHAR8 release_level; - - UCHAR8 attack_velocity:7; - UCHAR8 Unused7:1; - - UCHAR8 volume_velocity:7; - UCHAR8 Unused8:1; - - UCHAR8 keyboard_scaling:7; - UCHAR8 Unused9:1; -}; -typedef struct wf_envelope wavefront_envelope; - -struct wf_lfo -{ - UCHAR8 sample_number; - - UCHAR8 frequency:7; - UCHAR8 Unused1:1; - - UCHAR8 am_src:4; - UCHAR8 fm_src:4; - - CHAR8 fm_amount; - CHAR8 am_amount; - CHAR8 start_level; - CHAR8 end_level; - - UCHAR8 ramp_delay:7; - UCHAR8 wave_restart:1; /* for LFO2 only */ - - UCHAR8 ramp_time:7; - UCHAR8 Unused2:1; -}; -typedef struct wf_lfo wavefront_lfo; - -struct wf_patch -{ - INT16 frequency_bias; /* ** THIS IS IN MOTOROLA FORMAT!! ** */ - - UCHAR8 amplitude_bias:7; - UCHAR8 Unused1:1; - - UCHAR8 portamento:7; - UCHAR8 Unused2:1; - - UCHAR8 sample_number; - - UCHAR8 pitch_bend:4; - UCHAR8 sample_msb:1; - UCHAR8 Unused3:3; - - UCHAR8 mono:1; - UCHAR8 retrigger:1; - UCHAR8 nohold:1; - UCHAR8 restart:1; - UCHAR8 filterconfig:2; /* SDK says "not used" */ - UCHAR8 reuse:1; - UCHAR8 reset_lfo:1; - - UCHAR8 fm_src2:4; - UCHAR8 fm_src1:4; - - CHAR8 fm_amount1; - CHAR8 fm_amount2; - - UCHAR8 am_src:4; - UCHAR8 Unused4:4; - - CHAR8 am_amount; - - UCHAR8 fc1_mode:4; - UCHAR8 fc2_mode:4; - - CHAR8 fc1_mod_amount; - CHAR8 fc1_keyboard_scaling; - CHAR8 fc1_bias; - CHAR8 fc2_mod_amount; - CHAR8 fc2_keyboard_scaling; - CHAR8 fc2_bias; - - UCHAR8 randomizer:7; - UCHAR8 Unused5:1; - - struct wf_envelope envelope1; - struct wf_envelope envelope2; - struct wf_lfo lfo1; - struct wf_lfo lfo2; -}; -typedef struct wf_patch wavefront_patch; - -struct wf_layer -{ - UCHAR8 patch_number; - - UCHAR8 mix_level:7; - UCHAR8 mute:1; - - UCHAR8 split_point:7; - UCHAR8 play_below:1; - - UCHAR8 pan_mod_src:2; - UCHAR8 pan_or_mod:1; - UCHAR8 pan:4; - UCHAR8 split_type:1; -}; -typedef struct wf_layer wavefront_layer; - -struct wf_program -{ - struct wf_layer layer[WF_NUM_LAYERS]; -}; -typedef struct wf_program wavefront_program; - -struct wf_sample_offset -{ - INT32 Fraction:4; - INT32 Integer:20; - INT32 Unused:8; -}; -typedef struct wf_sample_offset wavefront_sample_offset; - -/* Sample slot types */ - -#define WF_ST_SAMPLE 0 -#define WF_ST_MULTISAMPLE 1 -#define WF_ST_ALIAS 2 -#define WF_ST_EMPTY 3 - -/* pseudo's */ - -#define WF_ST_DRUM 4 -#define WF_ST_PROGRAM 5 -#define WF_ST_PATCH 6 -#define WF_ST_SAMPLEHDR 7 - -#define WF_ST_MASK 0xf - -/* Flags for slot status. These occupy the upper bits of the same byte - as a sample type. -*/ - -#define WF_SLOT_USED 0x80 /* XXX don't rely on this being accurate */ -#define WF_SLOT_FILLED 0x40 -#define WF_SLOT_ROM 0x20 - -#define WF_SLOT_MASK 0xf0 - -/* channel constants */ - -#define WF_CH_MONO 0 -#define WF_CH_LEFT 1 -#define WF_CH_RIGHT 2 - -/* Sample formats */ - -#define LINEAR_16BIT 0 -#define WHITE_NOISE 1 -#define LINEAR_8BIT 2 -#define MULAW_8BIT 3 - -#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2) - - -/* - - Because most/all of the sample data we pass in via pointers has - never been copied (just mmap-ed into user space straight from the - disk), it would be nice to allow handling of multi-channel sample - data without forcing user-level extraction of the relevant bytes. - - So, we need a way of specifying which channel to use (the WaveFront - only handles mono samples in a given slot), and the only way to do - this without using some struct other than wavefront_sample as the - interface is the awful hack of using the unused bits in a - wavefront_sample: - - Val Meaning - --- ------- - 0 no channel selection (use channel 1, sample is MONO) - 1 use first channel, and skip one - 2 use second channel, and skip one - 3 use third channel, and skip two - 4 use fourth channel, skip three - 5 use fifth channel, skip four - 6 use six channel, skip five - - - This can handle up to 4 channels, and anyone downloading >4 channels - of sample data just to select one of them needs to find some tools - like sox ... - - NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is - important. - -*/ - -#define WF_SET_CHANNEL(samp,chn) \ - (samp)->Unused1 = chn & 0x1; \ - (samp)->Unused2 = chn & 0x2; \ - (samp)->Unused3 = chn & 0x4 - -#define WF_GET_CHANNEL(samp) \ - (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1) - -typedef struct wf_sample { - struct wf_sample_offset sampleStartOffset; - struct wf_sample_offset loopStartOffset; - struct wf_sample_offset loopEndOffset; - struct wf_sample_offset sampleEndOffset; - INT16 FrequencyBias; - UCHAR8 SampleResolution:2; /* sample_format */ - UCHAR8 Unused1:1; - UCHAR8 Loop:1; - UCHAR8 Bidirectional:1; - UCHAR8 Unused2:1; - UCHAR8 Reverse:1; - UCHAR8 Unused3:1; -} wavefront_sample; - -typedef struct wf_multisample { - INT16 NumberOfSamples; /* log2 of the number of samples */ - INT16 SampleNumber[NUM_MIDIKEYS]; -} wavefront_multisample; - -typedef struct wf_alias { - INT16 OriginalSample; - - struct wf_sample_offset sampleStartOffset; - struct wf_sample_offset loopStartOffset; - struct wf_sample_offset sampleEndOffset; - struct wf_sample_offset loopEndOffset; - - INT16 FrequencyBias; - - UCHAR8 SampleResolution:2; - UCHAR8 Unused1:1; - UCHAR8 Loop:1; - UCHAR8 Bidirectional:1; - UCHAR8 Unused2:1; - UCHAR8 Reverse:1; - UCHAR8 Unused3:1; - - /* This structure is meant to be padded only to 16 bits on their - original. Of course, whoever wrote their documentation didn't - realize that sizeof(struct) can be >= - sum(sizeof(struct-fields)) and so thought that giving a C level - description of the structs used in WavePatch files was - sufficient. I suppose it was, as long as you remember the - standard 16->32 bit issues. - */ - - UCHAR8 sixteen_bit_padding; -} __attribute__((packed)) wavefront_alias; - -typedef struct wf_drum { - UCHAR8 PatchNumber; - UCHAR8 MixLevel:7; - UCHAR8 Unmute:1; - UCHAR8 Group:4; - UCHAR8 Unused1:4; - UCHAR8 PanModSource:2; - UCHAR8 PanModulated:1; - UCHAR8 PanAmount:4; - UCHAR8 Unused2:1; -} wavefront_drum; - -typedef struct wf_drumkit { - struct wf_drum drum[NUM_MIDIKEYS]; -} wavefront_drumkit; - -typedef struct wf_channel_programs { - UCHAR8 Program[NUM_MIDICHANNELS]; -} wavefront_channel_programs; - -/* How to get MIDI channel status from the data returned by - a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs) -*/ - -#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7)) - -typedef union wf_any { - wavefront_sample s; - wavefront_multisample ms; - wavefront_alias a; - wavefront_program pr; - wavefront_patch p; - wavefront_drum d; -} wavefront_any; - -/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h - might work for other wave-table based patch loading situations. - Alas, his fears were correct. The WaveFront doesn't even come with - just "patches", but several different kind of structures that - control the sound generation process. - */ - -typedef struct wf_patch_info { - - /* the first two fields are used by the OSS "patch loading" interface - only, and are unused by the current user-level library. - */ - - INT16 key; /* Use WAVEFRONT_PATCH here */ - UINT16 devno; /* fill in when sending */ - UCHAR8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */ - -#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999 - - UINT16 number; /* patch/sample/prog number */ - - UINT32 size; /* size of any data included in - one of the fields in `hdrptr', or - as `dataptr'. - - NOTE: for actual samples, this is - the size of the *SELECTED CHANNEL* - even if more data is actually available. - - So, a stereo sample (2 channels) of - 6000 bytes total has `size' = 3000. - - See the macros and comments for - WF_{GET,SET}_CHANNEL above. - - */ - wavefront_any __user *hdrptr; /* user-space ptr to hdr bytes */ - UINT16 __user *dataptr; /* actual sample data */ - - wavefront_any hdr; /* kernel-space copy of hdr bytes */ -} wavefront_patch_info; - -/* The maximum number of bytes we will ever move to or from user space - in response to a WFC_* command. This obviously doesn't cover - actual sample data. -*/ - -#define WF_MAX_READ sizeof(wavefront_multisample) -#define WF_MAX_WRITE sizeof(wavefront_multisample) - -/* - This allows us to execute any WF command except the download/upload - ones, which are handled differently due to copyin/copyout issues as - well as data-nybbling to/from the card. - */ - -typedef struct wavefront_control { - int cmd; /* WFC_* */ - char status; /* return status to user-space */ - unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */ - unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */ -} wavefront_control; - -#define WFCTL_WFCMD 0x1 -#define WFCTL_LOAD_SPP 0x2 - -/* Modulator table */ - -#define WF_MOD_LFO1 0 -#define WF_MOD_LFO2 1 -#define WF_MOD_ENV1 2 -#define WF_MOD_ENV2 3 -#define WF_MOD_KEYBOARD 4 -#define WF_MOD_LOGKEY 5 -#define WF_MOD_VELOCITY 6 -#define WF_MOD_LOGVEL 7 -#define WF_MOD_RANDOM 8 -#define WF_MOD_PRESSURE 9 -#define WF_MOD_MOD_WHEEL 10 -#define WF_MOD_1 WF_MOD_MOD_WHEEL -#define WF_MOD_BREATH 11 -#define WF_MOD_2 WF_MOD_BREATH -#define WF_MOD_FOOT 12 -#define WF_MOD_4 WF_MOD_FOOT -#define WF_MOD_VOLUME 13 -#define WF_MOD_7 WF_MOD_VOLUME -#define WF_MOD_PAN 14 -#define WF_MOD_10 WF_MOD_PAN -#define WF_MOD_EXPR 15 -#define WF_MOD_11 WF_MOD_EXPR - -/* FX-related material */ - -typedef struct wf_fx_info { - int request; /* see list below */ - int data[4]; /* we don't need much */ -} wavefront_fx_info; - -/* support for each of these will be forthcoming once I or someone - else has figured out which of the addresses on page 6 and page 7 of - the YSS225 control each parameter. Incidentally, these come from - the Windows driver interface, but again, Turtle Beach didn't - document the API to use them. -*/ - -#define WFFX_SETOUTGAIN 0 -#define WFFX_SETSTEREOOUTGAIN 1 -#define WFFX_SETREVERBIN1GAIN 2 -#define WFFX_SETREVERBIN2GAIN 3 -#define WFFX_SETREVERBIN3GAIN 4 -#define WFFX_SETCHORUSINPORT 5 -#define WFFX_SETREVERBIN1PORT 6 -#define WFFX_SETREVERBIN2PORT 7 -#define WFFX_SETREVERBIN3PORT 8 -#define WFFX_SETEFFECTPORT 9 -#define WFFX_SETAUXPORT 10 -#define WFFX_SETREVERBTYPE 11 -#define WFFX_SETREVERBDELAY 12 -#define WFFX_SETCHORUSLFO 13 -#define WFFX_SETCHORUSPMD 14 -#define WFFX_SETCHORUSAMD 15 -#define WFFX_SETEFFECT 16 -#define WFFX_SETBASEALL 17 -#define WFFX_SETREVERBALL 18 -#define WFFX_SETCHORUSALL 20 -#define WFFX_SETREVERBDEF 22 -#define WFFX_SETCHORUSDEF 23 -#define WFFX_DELAYSETINGAIN 24 -#define WFFX_DELAYSETFBGAIN 25 -#define WFFX_DELAYSETFBLPF 26 -#define WFFX_DELAYSETGAIN 27 -#define WFFX_DELAYSETTIME 28 -#define WFFX_DELAYSETFBTIME 29 -#define WFFX_DELAYSETALL 30 -#define WFFX_DELAYSETDEF 32 -#define WFFX_SDELAYSETINGAIN 33 -#define WFFX_SDELAYSETFBGAIN 34 -#define WFFX_SDELAYSETFBLPF 35 -#define WFFX_SDELAYSETGAIN 36 -#define WFFX_SDELAYSETTIME 37 -#define WFFX_SDELAYSETFBTIME 38 -#define WFFX_SDELAYSETALL 39 -#define WFFX_SDELAYSETDEF 41 -#define WFFX_DEQSETINGAIN 42 -#define WFFX_DEQSETFILTER 43 -#define WFFX_DEQSETALL 44 -#define WFFX_DEQSETDEF 46 -#define WFFX_MUTE 47 -#define WFFX_FLANGESETBALANCE 48 -#define WFFX_FLANGESETDELAY 49 -#define WFFX_FLANGESETDWFFX_TH 50 -#define WFFX_FLANGESETFBGAIN 51 -#define WFFX_FLANGESETINGAIN 52 -#define WFFX_FLANGESETLFO 53 -#define WFFX_FLANGESETALL 54 -#define WFFX_FLANGESETDEF 56 -#define WFFX_PITCHSETSHIFT 57 -#define WFFX_PITCHSETBALANCE 58 -#define WFFX_PITCHSETALL 59 -#define WFFX_PITCHSETDEF 61 -#define WFFX_SRSSETINGAIN 62 -#define WFFX_SRSSETSPACE 63 -#define WFFX_SRSSETCENTER 64 -#define WFFX_SRSSETGAIN 65 -#define WFFX_SRSSETMODE 66 -#define WFFX_SRSSETDEF 68 - -/* Allow direct user-space control over FX memory/coefficient data. - In theory this could be used to download the FX microprogram, - but it would be a little slower, and involve some weird code. - */ - -#define WFFX_MEMSET 69 - -#endif /* __wavefront_h__ */ diff --git a/kernel/Makefile b/kernel/Makefile index d948ca12ac..5e3f3b7556 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -8,7 +8,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o + hrtimer.o rwsem.o latency.o nsproxy.o srcu.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 736cb0bd49..4cf65f5c6a 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -17,6 +17,69 @@ #include "internals.h" +/** + * dynamic_irq_init - initialize a dynamically allocated irq + * @irq: irq number to initialize + */ +void dynamic_irq_init(unsigned int irq) +{ + struct irq_desc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to initialize invalid IRQ%d\n", irq); + WARN_ON(1); + return; + } + + /* Ensure we don't have left over values from a previous use of this irq */ + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock, flags); + desc->status = IRQ_DISABLED; + desc->chip = &no_irq_chip; + desc->handle_irq = handle_bad_irq; + desc->depth = 1; + desc->handler_data = NULL; + desc->chip_data = NULL; + desc->action = NULL; + desc->irq_count = 0; + desc->irqs_unhandled = 0; +#ifdef CONFIG_SMP + desc->affinity = CPU_MASK_ALL; +#endif + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * dynamic_irq_cleanup - cleanup a dynamically allocated irq + * @irq: irq number to initialize + */ +void dynamic_irq_cleanup(unsigned int irq) +{ + struct irq_desc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to cleanup invalid IRQ%d\n", irq); + WARN_ON(1); + return; + } + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock, flags); + if (desc->action) { + spin_unlock_irqrestore(&desc->lock, flags); + printk(KERN_ERR "Destroying IRQ%d without calling free_irq\n", + irq); + WARN_ON(1); + return; + } + desc->handle_irq = handle_bad_irq; + desc->chip = &no_irq_chip; + spin_unlock_irqrestore(&desc->lock, flags); +} + + /** * set_irq_chip - set the irq chip for an irq * @irq: irq number diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index a57ebe9fa6..4baa3bbcd2 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -7,17 +7,17 @@ void set_pending_irq(unsigned int irq, cpumask_t mask) unsigned long flags; spin_lock_irqsave(&desc->lock, flags); - desc->move_irq = 1; + desc->status |= IRQ_MOVE_PENDING; irq_desc[irq].pending_mask = mask; spin_unlock_irqrestore(&desc->lock, flags); } -void move_native_irq(int irq) +void move_masked_irq(int irq) { struct irq_desc *desc = irq_desc + irq; cpumask_t tmp; - if (likely(!desc->move_irq)) + if (likely(!(desc->status & IRQ_MOVE_PENDING))) return; /* @@ -28,7 +28,7 @@ void move_native_irq(int irq) return; } - desc->move_irq = 0; + desc->status &= ~IRQ_MOVE_PENDING; if (unlikely(cpus_empty(irq_desc[irq].pending_mask))) return; @@ -48,15 +48,29 @@ void move_native_irq(int irq) * when an active trigger is comming in. This could * cause some ioapics to mal-function. * Being paranoid i guess! + * + * For correct operation this depends on the caller + * masking the irqs. */ if (likely(!cpus_empty(tmp))) { - if (likely(!(desc->status & IRQ_DISABLED))) - desc->chip->disable(irq); - desc->chip->set_affinity(irq,tmp); - - if (likely(!(desc->status & IRQ_DISABLED))) - desc->chip->enable(irq); } cpus_clear(irq_desc[irq].pending_mask); } + +void move_native_irq(int irq) +{ + struct irq_desc *desc = irq_desc + irq; + + if (likely(!(desc->status & IRQ_MOVE_PENDING))) + return; + + if (likely(!(desc->status & IRQ_DISABLED))) + desc->chip->disable(irq); + + move_masked_irq(irq); + + if (likely(!(desc->status & IRQ_DISABLED))) + desc->chip->enable(irq); +} + diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 523e46483b..26bb5ffe1e 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -71,9 +71,6 @@ static DEFINE_PER_CPU(struct tasklet_struct, rcu_tasklet) = {NULL}; static int blimit = 10; static int qhimark = 10000; static int qlowmark = 100; -#ifdef CONFIG_SMP -static int rsinterval = 1000; -#endif static atomic_t rcu_barrier_cpu_count; static DEFINE_MUTEX(rcu_barrier_mutex); @@ -86,8 +83,8 @@ static void force_quiescent_state(struct rcu_data *rdp, int cpu; cpumask_t cpumask; set_need_resched(); - if (unlikely(rdp->qlen - rdp->last_rs_qlen > rsinterval)) { - rdp->last_rs_qlen = rdp->qlen; + if (unlikely(!rcp->signaled)) { + rcp->signaled = 1; /* * Don't send IPI to itself. With irqs disabled, * rdp->cpu is the current cpu. @@ -301,6 +298,7 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) smp_mb(); cpus_andnot(rcp->cpumask, cpu_online_map, nohz_cpu_mask); + rcp->signaled = 0; } } @@ -628,9 +626,6 @@ void synchronize_rcu(void) module_param(blimit, int, 0); module_param(qhimark, int, 0); module_param(qlowmark, int, 0); -#ifdef CONFIG_SMP -module_param(rsinterval, int, 0); -#endif EXPORT_SYMBOL_GPL(rcu_batches_completed); EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); EXPORT_SYMBOL_GPL(call_rcu); diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 23446e91cd..e2bda18f6f 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -15,9 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) IBM Corporation, 2005 + * Copyright (C) IBM Corporation, 2005, 2006 * * Authors: Paul E. McKenney + * Josh Triplett * * See also: Documentation/RCU/torture.txt */ @@ -44,19 +45,25 @@ #include #include #include +#include MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Paul E. McKenney and " + "Josh Triplett "); static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ +static int nfakewriters = 4; /* # fake writer threads */ static int stat_interval; /* Interval between stats, in seconds. */ /* Defaults to "only at end of test". */ static int verbose; /* Print more debug info. */ static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ -static char *torture_type = "rcu"; /* What to torture. */ +static char *torture_type = "rcu"; /* What RCU implementation to torture. */ module_param(nreaders, int, 0); MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); +module_param(nfakewriters, int, 0); +MODULE_PARM_DESC(nfakewriters, "Number of RCU fake writer threads"); module_param(stat_interval, int, 0); MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); module_param(verbose, bool, 0); @@ -66,7 +73,7 @@ MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); module_param(shuffle_interval, int, 0); MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); module_param(torture_type, charp, 0); -MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh)"); +MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); #define TORTURE_FLAG "-torture:" #define PRINTK_STRING(s) \ @@ -80,6 +87,7 @@ static char printk_buf[4096]; static int nrealreaders; static struct task_struct *writer_task; +static struct task_struct **fakewriter_tasks; static struct task_struct **reader_tasks; static struct task_struct *stats_task; static struct task_struct *shuffler_task; @@ -104,11 +112,12 @@ static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) = { 0 }; static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1]; -atomic_t n_rcu_torture_alloc; -atomic_t n_rcu_torture_alloc_fail; -atomic_t n_rcu_torture_free; -atomic_t n_rcu_torture_mberror; -atomic_t n_rcu_torture_error; +static atomic_t n_rcu_torture_alloc; +static atomic_t n_rcu_torture_alloc_fail; +static atomic_t n_rcu_torture_free; +static atomic_t n_rcu_torture_mberror; +static atomic_t n_rcu_torture_error; +static struct list_head rcu_torture_removed; /* * Allocate an element from the rcu_tortures pool. @@ -145,7 +154,7 @@ rcu_torture_free(struct rcu_torture *p) struct rcu_random_state { unsigned long rrs_state; - unsigned long rrs_count; + long rrs_count; }; #define RCU_RANDOM_MULT 39916801 /* prime */ @@ -158,7 +167,7 @@ struct rcu_random_state { * Crude but fast random-number generator. Uses a linear congruential * generator, with occasional help from get_random_bytes(). */ -static long +static unsigned long rcu_random(struct rcu_random_state *rrsp) { long refresh; @@ -180,9 +189,11 @@ struct rcu_torture_ops { void (*init)(void); void (*cleanup)(void); int (*readlock)(void); + void (*readdelay)(struct rcu_random_state *rrsp); void (*readunlock)(int idx); int (*completed)(void); void (*deferredfree)(struct rcu_torture *p); + void (*sync)(void); int (*stats)(char *page); char *name; }; @@ -198,6 +209,18 @@ static int rcu_torture_read_lock(void) __acquires(RCU) return 0; } +static void rcu_read_delay(struct rcu_random_state *rrsp) +{ + long delay; + const long longdelay = 200; + + /* We want there to be long-running readers, but not all the time. */ + + delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay); + if (!delay) + udelay(longdelay); +} + static void rcu_torture_read_unlock(int idx) __releases(RCU) { rcu_read_unlock(); @@ -239,13 +262,54 @@ static struct rcu_torture_ops rcu_ops = { .init = NULL, .cleanup = NULL, .readlock = rcu_torture_read_lock, + .readdelay = rcu_read_delay, .readunlock = rcu_torture_read_unlock, .completed = rcu_torture_completed, .deferredfree = rcu_torture_deferred_free, + .sync = synchronize_rcu, .stats = NULL, .name = "rcu" }; +static void rcu_sync_torture_deferred_free(struct rcu_torture *p) +{ + int i; + struct rcu_torture *rp; + struct rcu_torture *rp1; + + cur_ops->sync(); + list_add(&p->rtort_free, &rcu_torture_removed); + list_for_each_entry_safe(rp, rp1, &rcu_torture_removed, rtort_free) { + i = rp->rtort_pipe_count; + if (i > RCU_TORTURE_PIPE_LEN) + i = RCU_TORTURE_PIPE_LEN; + atomic_inc(&rcu_torture_wcount[i]); + if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { + rp->rtort_mbtest = 0; + list_del(&rp->rtort_free); + rcu_torture_free(rp); + } + } +} + +static void rcu_sync_torture_init(void) +{ + INIT_LIST_HEAD(&rcu_torture_removed); +} + +static struct rcu_torture_ops rcu_sync_ops = { + .init = rcu_sync_torture_init, + .cleanup = NULL, + .readlock = rcu_torture_read_lock, + .readdelay = rcu_read_delay, + .readunlock = rcu_torture_read_unlock, + .completed = rcu_torture_completed, + .deferredfree = rcu_sync_torture_deferred_free, + .sync = synchronize_rcu, + .stats = NULL, + .name = "rcu_sync" +}; + /* * Definitions for rcu_bh torture testing. */ @@ -271,19 +335,176 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p) call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); } +struct rcu_bh_torture_synchronize { + struct rcu_head head; + struct completion completion; +}; + +static void rcu_bh_torture_wakeme_after_cb(struct rcu_head *head) +{ + struct rcu_bh_torture_synchronize *rcu; + + rcu = container_of(head, struct rcu_bh_torture_synchronize, head); + complete(&rcu->completion); +} + +static void rcu_bh_torture_synchronize(void) +{ + struct rcu_bh_torture_synchronize rcu; + + init_completion(&rcu.completion); + call_rcu_bh(&rcu.head, rcu_bh_torture_wakeme_after_cb); + wait_for_completion(&rcu.completion); +} + static struct rcu_torture_ops rcu_bh_ops = { .init = NULL, .cleanup = NULL, .readlock = rcu_bh_torture_read_lock, + .readdelay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = rcu_bh_torture_read_unlock, .completed = rcu_bh_torture_completed, .deferredfree = rcu_bh_torture_deferred_free, + .sync = rcu_bh_torture_synchronize, .stats = NULL, .name = "rcu_bh" }; +static struct rcu_torture_ops rcu_bh_sync_ops = { + .init = rcu_sync_torture_init, + .cleanup = NULL, + .readlock = rcu_bh_torture_read_lock, + .readdelay = rcu_read_delay, /* just reuse rcu's version. */ + .readunlock = rcu_bh_torture_read_unlock, + .completed = rcu_bh_torture_completed, + .deferredfree = rcu_sync_torture_deferred_free, + .sync = rcu_bh_torture_synchronize, + .stats = NULL, + .name = "rcu_bh_sync" +}; + +/* + * Definitions for srcu torture testing. + */ + +static struct srcu_struct srcu_ctl; + +static void srcu_torture_init(void) +{ + init_srcu_struct(&srcu_ctl); + rcu_sync_torture_init(); +} + +static void srcu_torture_cleanup(void) +{ + synchronize_srcu(&srcu_ctl); + cleanup_srcu_struct(&srcu_ctl); +} + +static int srcu_torture_read_lock(void) +{ + return srcu_read_lock(&srcu_ctl); +} + +static void srcu_read_delay(struct rcu_random_state *rrsp) +{ + long delay; + const long uspertick = 1000000 / HZ; + const long longdelay = 10; + + /* We want there to be long-running readers, but not all the time. */ + + delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick); + if (!delay) + schedule_timeout_interruptible(longdelay); +} + +static void srcu_torture_read_unlock(int idx) +{ + srcu_read_unlock(&srcu_ctl, idx); +} + +static int srcu_torture_completed(void) +{ + return srcu_batches_completed(&srcu_ctl); +} + +static void srcu_torture_synchronize(void) +{ + synchronize_srcu(&srcu_ctl); +} + +static int srcu_torture_stats(char *page) +{ + int cnt = 0; + int cpu; + int idx = srcu_ctl.completed & 0x1; + + cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):", + torture_type, TORTURE_FLAG, idx); + for_each_possible_cpu(cpu) { + cnt += sprintf(&page[cnt], " %d(%d,%d)", cpu, + per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx], + per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]); + } + cnt += sprintf(&page[cnt], "\n"); + return cnt; +} + +static struct rcu_torture_ops srcu_ops = { + .init = srcu_torture_init, + .cleanup = srcu_torture_cleanup, + .readlock = srcu_torture_read_lock, + .readdelay = srcu_read_delay, + .readunlock = srcu_torture_read_unlock, + .completed = srcu_torture_completed, + .deferredfree = rcu_sync_torture_deferred_free, + .sync = srcu_torture_synchronize, + .stats = srcu_torture_stats, + .name = "srcu" +}; + +/* + * Definitions for sched torture testing. + */ + +static int sched_torture_read_lock(void) +{ + preempt_disable(); + return 0; +} + +static void sched_torture_read_unlock(int idx) +{ + preempt_enable(); +} + +static int sched_torture_completed(void) +{ + return 0; +} + +static void sched_torture_synchronize(void) +{ + synchronize_sched(); +} + +static struct rcu_torture_ops sched_ops = { + .init = rcu_sync_torture_init, + .cleanup = NULL, + .readlock = sched_torture_read_lock, + .readdelay = rcu_read_delay, /* just reuse rcu's version. */ + .readunlock = sched_torture_read_unlock, + .completed = sched_torture_completed, + .deferredfree = rcu_sync_torture_deferred_free, + .sync = sched_torture_synchronize, + .stats = NULL, + .name = "sched" +}; + static struct rcu_torture_ops *torture_ops[] = - { &rcu_ops, &rcu_bh_ops, NULL }; + { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, &srcu_ops, + &sched_ops, NULL }; /* * RCU torture writer kthread. Repeatedly substitutes a new structure @@ -329,6 +550,30 @@ rcu_torture_writer(void *arg) return 0; } +/* + * RCU torture fake writer kthread. Repeatedly calls sync, with a random + * delay between calls. + */ +static int +rcu_torture_fakewriter(void *arg) +{ + DEFINE_RCU_RANDOM(rand); + + VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started"); + set_user_nice(current, 19); + + do { + schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); + udelay(rcu_random(&rand) & 0x3ff); + cur_ops->sync(); + } while (!kthread_should_stop() && !fullstop); + + VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); + while (!kthread_should_stop()) + schedule_timeout_uninterruptible(1); + return 0; +} + /* * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, * incrementing the corresponding element of the pipeline array. The @@ -359,7 +604,7 @@ rcu_torture_reader(void *arg) } if (p->rtort_mbtest == 0) atomic_inc(&n_rcu_torture_mberror); - udelay(rcu_random(&rand) & 0x7f); + cur_ops->readdelay(&rand); preempt_disable(); pipe_count = p->rtort_pipe_count; if (pipe_count > RCU_TORTURE_PIPE_LEN) { @@ -483,7 +728,7 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ /* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case * is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs. */ -void rcu_torture_shuffle_tasks(void) +static void rcu_torture_shuffle_tasks(void) { cpumask_t tmp_mask = CPU_MASK_ALL; int i; @@ -507,6 +752,12 @@ void rcu_torture_shuffle_tasks(void) set_cpus_allowed(reader_tasks[i], tmp_mask); } + if (fakewriter_tasks != NULL) { + for (i = 0; i < nfakewriters; i++) + if (fakewriter_tasks[i]) + set_cpus_allowed(fakewriter_tasks[i], tmp_mask); + } + if (writer_task) set_cpus_allowed(writer_task, tmp_mask); @@ -540,11 +791,12 @@ rcu_torture_shuffle(void *arg) static inline void rcu_torture_print_module_parms(char *tag) { - printk(KERN_ALERT "%s" TORTURE_FLAG "--- %s: nreaders=%d " + printk(KERN_ALERT "%s" TORTURE_FLAG + "--- %s: nreaders=%d nfakewriters=%d " "stat_interval=%d verbose=%d test_no_idle_hz=%d " "shuffle_interval = %d\n", - torture_type, tag, nrealreaders, stat_interval, verbose, - test_no_idle_hz, shuffle_interval); + torture_type, tag, nrealreaders, nfakewriters, + stat_interval, verbose, test_no_idle_hz, shuffle_interval); } static void @@ -579,6 +831,19 @@ rcu_torture_cleanup(void) } rcu_torture_current = NULL; + if (fakewriter_tasks != NULL) { + for (i = 0; i < nfakewriters; i++) { + if (fakewriter_tasks[i] != NULL) { + VERBOSE_PRINTK_STRING( + "Stopping rcu_torture_fakewriter task"); + kthread_stop(fakewriter_tasks[i]); + } + fakewriter_tasks[i] = NULL; + } + kfree(fakewriter_tasks); + fakewriter_tasks = NULL; + } + if (stats_task != NULL) { VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task"); kthread_stop(stats_task); @@ -666,7 +931,25 @@ rcu_torture_init(void) writer_task = NULL; goto unwind; } - reader_tasks = kmalloc(nrealreaders * sizeof(reader_tasks[0]), + fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]), + GFP_KERNEL); + if (fakewriter_tasks == NULL) { + VERBOSE_PRINTK_ERRSTRING("out of memory"); + firsterr = -ENOMEM; + goto unwind; + } + for (i = 0; i < nfakewriters; i++) { + VERBOSE_PRINTK_STRING("Creating rcu_torture_fakewriter task"); + fakewriter_tasks[i] = kthread_run(rcu_torture_fakewriter, NULL, + "rcu_torture_fakewriter"); + if (IS_ERR(fakewriter_tasks[i])) { + firsterr = PTR_ERR(fakewriter_tasks[i]); + VERBOSE_PRINTK_ERRSTRING("Failed to create fakewriter"); + fakewriter_tasks[i] = NULL; + goto unwind; + } + } + reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]), GFP_KERNEL); if (reader_tasks == NULL) { VERBOSE_PRINTK_ERRSTRING("out of memory"); diff --git a/kernel/srcu.c b/kernel/srcu.c new file mode 100644 index 0000000000..3507cabe96 --- /dev/null +++ b/kernel/srcu.c @@ -0,0 +1,258 @@ +/* + * Sleepable Read-Copy Update mechanism for mutual exclusion. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Paul McKenney + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU/ *.txt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * init_srcu_struct - initialize a sleep-RCU structure + * @sp: structure to initialize. + * + * Must invoke this on a given srcu_struct before passing that srcu_struct + * to any other function. Each srcu_struct represents a separate domain + * of SRCU protection. + */ +int init_srcu_struct(struct srcu_struct *sp) +{ + sp->completed = 0; + mutex_init(&sp->mutex); + sp->per_cpu_ref = alloc_percpu(struct srcu_struct_array); + return (sp->per_cpu_ref ? 0 : -ENOMEM); +} + +/* + * srcu_readers_active_idx -- returns approximate number of readers + * active on the specified rank of per-CPU counters. + */ + +static int srcu_readers_active_idx(struct srcu_struct *sp, int idx) +{ + int cpu; + int sum; + + sum = 0; + for_each_possible_cpu(cpu) + sum += per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]; + return sum; +} + +/** + * srcu_readers_active - returns approximate number of readers. + * @sp: which srcu_struct to count active readers (holding srcu_read_lock). + * + * Note that this is not an atomic primitive, and can therefore suffer + * severe errors when invoked on an active srcu_struct. That said, it + * can be useful as an error check at cleanup time. + */ +int srcu_readers_active(struct srcu_struct *sp) +{ + return srcu_readers_active_idx(sp, 0) + srcu_readers_active_idx(sp, 1); +} + +/** + * cleanup_srcu_struct - deconstruct a sleep-RCU structure + * @sp: structure to clean up. + * + * Must invoke this after you are finished using a given srcu_struct that + * was initialized via init_srcu_struct(), else you leak memory. + */ +void cleanup_srcu_struct(struct srcu_struct *sp) +{ + int sum; + + sum = srcu_readers_active(sp); + WARN_ON(sum); /* Leakage unless caller handles error. */ + if (sum != 0) + return; + free_percpu(sp->per_cpu_ref); + sp->per_cpu_ref = NULL; +} + +/** + * srcu_read_lock - register a new reader for an SRCU-protected structure. + * @sp: srcu_struct in which to register the new reader. + * + * Counts the new reader in the appropriate per-CPU element of the + * srcu_struct. Must be called from process context. + * Returns an index that must be passed to the matching srcu_read_unlock(). + */ +int srcu_read_lock(struct srcu_struct *sp) +{ + int idx; + + preempt_disable(); + idx = sp->completed & 0x1; + barrier(); /* ensure compiler looks -once- at sp->completed. */ + per_cpu_ptr(sp->per_cpu_ref, smp_processor_id())->c[idx]++; + srcu_barrier(); /* ensure compiler won't misorder critical section. */ + preempt_enable(); + return idx; +} + +/** + * srcu_read_unlock - unregister a old reader from an SRCU-protected structure. + * @sp: srcu_struct in which to unregister the old reader. + * @idx: return value from corresponding srcu_read_lock(). + * + * Removes the count for the old reader from the appropriate per-CPU + * element of the srcu_struct. Note that this may well be a different + * CPU than that which was incremented by the corresponding srcu_read_lock(). + * Must be called from process context. + */ +void srcu_read_unlock(struct srcu_struct *sp, int idx) +{ + preempt_disable(); + srcu_barrier(); /* ensure compiler won't misorder critical section. */ + per_cpu_ptr(sp->per_cpu_ref, smp_processor_id())->c[idx]--; + preempt_enable(); +} + +/** + * synchronize_srcu - wait for prior SRCU read-side critical-section completion + * @sp: srcu_struct with which to synchronize. + * + * Flip the completed counter, and wait for the old count to drain to zero. + * As with classic RCU, the updater must use some separate means of + * synchronizing concurrent updates. Can block; must be called from + * process context. + * + * Note that it is illegal to call synchornize_srcu() from the corresponding + * SRCU read-side critical section; doing so will result in deadlock. + * However, it is perfectly legal to call synchronize_srcu() on one + * srcu_struct from some other srcu_struct's read-side critical section. + */ +void synchronize_srcu(struct srcu_struct *sp) +{ + int idx; + + idx = sp->completed; + mutex_lock(&sp->mutex); + + /* + * Check to see if someone else did the work for us while we were + * waiting to acquire the lock. We need -two- advances of + * the counter, not just one. If there was but one, we might have + * shown up -after- our helper's first synchronize_sched(), thus + * having failed to prevent CPU-reordering races with concurrent + * srcu_read_unlock()s on other CPUs (see comment below). So we + * either (1) wait for two or (2) supply the second ourselves. + */ + + if ((sp->completed - idx) >= 2) { + mutex_unlock(&sp->mutex); + return; + } + + synchronize_sched(); /* Force memory barrier on all CPUs. */ + + /* + * The preceding synchronize_sched() ensures that any CPU that + * sees the new value of sp->completed will also see any preceding + * changes to data structures made by this CPU. This prevents + * some other CPU from reordering the accesses in its SRCU + * read-side critical section to precede the corresponding + * srcu_read_lock() -- ensuring that such references will in + * fact be protected. + * + * So it is now safe to do the flip. + */ + + idx = sp->completed & 0x1; + sp->completed++; + + synchronize_sched(); /* Force memory barrier on all CPUs. */ + + /* + * At this point, because of the preceding synchronize_sched(), + * all srcu_read_lock() calls using the old counters have completed. + * Their corresponding critical sections might well be still + * executing, but the srcu_read_lock() primitives themselves + * will have finished executing. + */ + + while (srcu_readers_active_idx(sp, idx)) + schedule_timeout_interruptible(1); + + synchronize_sched(); /* Force memory barrier on all CPUs. */ + + /* + * The preceding synchronize_sched() forces all srcu_read_unlock() + * primitives that were executing concurrently with the preceding + * for_each_possible_cpu() loop to have completed by this point. + * More importantly, it also forces the corresponding SRCU read-side + * critical sections to have also completed, and the corresponding + * references to SRCU-protected data items to be dropped. + * + * Note: + * + * Despite what you might think at first glance, the + * preceding synchronize_sched() -must- be within the + * critical section ended by the following mutex_unlock(). + * Otherwise, a task taking the early exit can race + * with a srcu_read_unlock(), which might have executed + * just before the preceding srcu_readers_active() check, + * and whose CPU might have reordered the srcu_read_unlock() + * with the preceding critical section. In this case, there + * is nothing preventing the synchronize_sched() task that is + * taking the early exit from freeing a data structure that + * is still being referenced (out of order) by the task + * doing the srcu_read_unlock(). + * + * Alternatively, the comparison with "2" on the early exit + * could be changed to "3", but this increases synchronize_srcu() + * latency for bulk loads. So the current code is preferred. + */ + + mutex_unlock(&sp->mutex); +} + +/** + * srcu_batches_completed - return batches completed. + * @sp: srcu_struct on which to report batch completion. + * + * Report the number of batches, correlated with, but not necessarily + * precisely the same as, the number of grace periods that have elapsed. + */ + +long srcu_batches_completed(struct srcu_struct *sp) +{ + return sp->completed; +} + +EXPORT_SYMBOL_GPL(init_srcu_struct); +EXPORT_SYMBOL_GPL(cleanup_srcu_struct); +EXPORT_SYMBOL_GPL(srcu_read_lock); +EXPORT_SYMBOL_GPL(srcu_read_unlock); +EXPORT_SYMBOL_GPL(synchronize_srcu); +EXPORT_SYMBOL_GPL(srcu_batches_completed); +EXPORT_SYMBOL_GPL(srcu_readers_active); diff --git a/kernel/sys.c b/kernel/sys.c index 2314867ae3..98489d8280 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -153,7 +153,7 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl, /* * Atomic notifier chain routines. Registration and unregistration - * use a mutex, and call_chain is synchronized by RCU (no locks). + * use a spinlock, and call_chain is synchronized by RCU (no locks). */ /** @@ -401,6 +401,129 @@ int raw_notifier_call_chain(struct raw_notifier_head *nh, EXPORT_SYMBOL_GPL(raw_notifier_call_chain); +/* + * SRCU notifier chain routines. Registration and unregistration + * use a mutex, and call_chain is synchronized by SRCU (no locks). + */ + +/** + * srcu_notifier_chain_register - Add notifier to an SRCU notifier chain + * @nh: Pointer to head of the SRCU notifier chain + * @n: New entry in notifier chain + * + * Adds a notifier to an SRCU notifier chain. + * Must be called in process context. + * + * Currently always returns zero. + */ + +int srcu_notifier_chain_register(struct srcu_notifier_head *nh, + struct notifier_block *n) +{ + int ret; + + /* + * This code gets used during boot-up, when task switching is + * not yet working and interrupts must remain disabled. At + * such times we must not call mutex_lock(). + */ + if (unlikely(system_state == SYSTEM_BOOTING)) + return notifier_chain_register(&nh->head, n); + + mutex_lock(&nh->mutex); + ret = notifier_chain_register(&nh->head, n); + mutex_unlock(&nh->mutex); + return ret; +} + +EXPORT_SYMBOL_GPL(srcu_notifier_chain_register); + +/** + * srcu_notifier_chain_unregister - Remove notifier from an SRCU notifier chain + * @nh: Pointer to head of the SRCU notifier chain + * @n: Entry to remove from notifier chain + * + * Removes a notifier from an SRCU notifier chain. + * Must be called from process context. + * + * Returns zero on success or %-ENOENT on failure. + */ +int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, + struct notifier_block *n) +{ + int ret; + + /* + * This code gets used during boot-up, when task switching is + * not yet working and interrupts must remain disabled. At + * such times we must not call mutex_lock(). + */ + if (unlikely(system_state == SYSTEM_BOOTING)) + return notifier_chain_unregister(&nh->head, n); + + mutex_lock(&nh->mutex); + ret = notifier_chain_unregister(&nh->head, n); + mutex_unlock(&nh->mutex); + synchronize_srcu(&nh->srcu); + return ret; +} + +EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); + +/** + * srcu_notifier_call_chain - Call functions in an SRCU notifier chain + * @nh: Pointer to head of the SRCU notifier chain + * @val: Value passed unmodified to notifier function + * @v: Pointer passed unmodified to notifier function + * + * Calls each function in a notifier chain in turn. The functions + * run in a process context, so they are allowed to block. + * + * If the return value of the notifier can be and'ed + * with %NOTIFY_STOP_MASK then srcu_notifier_call_chain + * will return immediately, with the return value of + * the notifier function which halted execution. + * Otherwise the return value is the return value + * of the last notifier function called. + */ + +int srcu_notifier_call_chain(struct srcu_notifier_head *nh, + unsigned long val, void *v) +{ + int ret; + int idx; + + idx = srcu_read_lock(&nh->srcu); + ret = notifier_call_chain(&nh->head, val, v); + srcu_read_unlock(&nh->srcu, idx); + return ret; +} + +EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); + +/** + * srcu_init_notifier_head - Initialize an SRCU notifier head + * @nh: Pointer to head of the srcu notifier chain + * + * Unlike other sorts of notifier heads, SRCU notifier heads require + * dynamic initialization. Be sure to call this routine before + * calling any of the other SRCU notifier routines for this head. + * + * If an SRCU notifier head is deallocated, it must first be cleaned + * up by calling srcu_cleanup_notifier_head(). Otherwise the head's + * per-cpu data (used by the SRCU mechanism) will leak. + */ + +void srcu_init_notifier_head(struct srcu_notifier_head *nh) +{ + mutex_init(&nh->mutex); + if (init_srcu_struct(&nh->srcu) < 0) + BUG(); + nh->head = NULL; +} + +EXPORT_SYMBOL_GPL(srcu_init_notifier_head); + /** * register_reboot_notifier - Register function to be called at reboot time * @nb: Info about notifier function to be called diff --git a/mm/filemap.c b/mm/filemap.c index ec46923598..f789500406 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1139,11 +1139,11 @@ success: } /** - * __generic_file_aio_read - generic filesystem read routine + * generic_file_aio_read - generic filesystem read routine * @iocb: kernel I/O control block * @iov: io vector request * @nr_segs: number of segments in the iovec - * @ppos: current file position + * @pos: current file position * * This is the "read()" routine for all filesystems * that can use the page cache directly. diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 7c7d03dbf7..1d709ff528 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -364,6 +364,8 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, pte_t *ptep; pte_t pte; struct page *page; + struct page *tmp; + LIST_HEAD(page_list); WARN_ON(!is_vm_hugetlb_page(vma)); BUG_ON(start & ~HPAGE_MASK); @@ -384,12 +386,16 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, continue; page = pte_page(pte); - put_page(page); + list_add(&page->lru, &page_list); add_mm_counter(mm, file_rss, (int) -(HPAGE_SIZE / PAGE_SIZE)); } spin_unlock(&mm->page_table_lock); flush_tlb_range(vma, start, end); + list_for_each_entry_safe(page, tmp, &page_list, lru) { + list_del(&page->lru); + put_page(page); + } } static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 4f59d90b81..a8c003e7b3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -900,7 +900,8 @@ int zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags) { /* free_pages my go negative - that's OK */ - long min = mark, free_pages = z->free_pages - (1 << order) + 1; + unsigned long min = mark; + long free_pages = z->free_pages - (1 << order) + 1; int o; if (alloc_flags & ALLOC_HIGH) @@ -2050,8 +2051,8 @@ int __init early_pfn_to_nid(unsigned long pfn) /** * free_bootmem_with_active_regions - Call free_bootmem_node for each active range - * @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed - * @max_low_pfn: The highest PFN that till be passed to free_bootmem_node + * @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed. + * @max_low_pfn: The highest PFN that will be passed to free_bootmem_node * * If an architecture guarantees that all ranges registered with * add_active_ranges() contain no holes and may be freed, this @@ -2081,11 +2082,11 @@ void __init free_bootmem_with_active_regions(int nid, /** * sparse_memory_present_with_active_regions - Call memory_present for each active range - * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used + * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used. * * If an architecture guarantees that all ranges registered with * add_active_ranges() contain no holes and may be freed, this - * this function may be used instead of calling memory_present() manually. + * function may be used instead of calling memory_present() manually. */ void __init sparse_memory_present_with_active_regions(int nid) { @@ -2155,14 +2156,14 @@ static void __init account_node_boundary(unsigned int nid, /** * get_pfn_range_for_nid - Return the start and end page frames for a node - * @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned - * @start_pfn: Passed by reference. On return, it will have the node start_pfn - * @end_pfn: Passed by reference. On return, it will have the node end_pfn + * @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned. + * @start_pfn: Passed by reference. On return, it will have the node start_pfn. + * @end_pfn: Passed by reference. On return, it will have the node end_pfn. * * It returns the start and end page frame of a node based on information * provided by an arch calling add_active_range(). If called for a node * with no available memory, a warning is printed and the start and end - * PFNs will be 0 + * PFNs will be 0. */ void __init get_pfn_range_for_nid(unsigned int nid, unsigned long *start_pfn, unsigned long *end_pfn) @@ -2215,7 +2216,7 @@ unsigned long __init zone_spanned_pages_in_node(int nid, /* * Return the number of holes in a range on a node. If nid is MAX_NUMNODES, - * then all holes in the requested range will be accounted for + * then all holes in the requested range will be accounted for. */ unsigned long __init __absent_pages_in_range(int nid, unsigned long range_start_pfn, @@ -2268,7 +2269,7 @@ unsigned long __init __absent_pages_in_range(int nid, * @start_pfn: The start PFN to start searching for holes * @end_pfn: The end PFN to stop searching for holes * - * It returns the number of pages frames in memory holes within a range + * It returns the number of pages frames in memory holes within a range. */ unsigned long __init absent_pages_in_range(unsigned long start_pfn, unsigned long end_pfn) @@ -2582,11 +2583,12 @@ void __init shrink_active_range(unsigned int nid, unsigned long old_end_pfn, /** * remove_all_active_ranges - Remove all currently registered regions + * * During discovery, it may be found that a table like SRAT is invalid * and an alternative discovery method must be used. This function removes * all currently registered regions. */ -void __init remove_all_active_ranges() +void __init remove_all_active_ranges(void) { memset(early_node_map, 0, sizeof(early_node_map)); nr_nodemap_entries = 0; @@ -2636,7 +2638,7 @@ unsigned long __init find_min_pfn_for_node(unsigned long nid) * find_min_pfn_with_active_regions - Find the minimum PFN registered * * It returns the minimum PFN based on information provided via - * add_active_range() + * add_active_range(). */ unsigned long __init find_min_pfn_with_active_regions(void) { @@ -2647,7 +2649,7 @@ unsigned long __init find_min_pfn_with_active_regions(void) * find_max_pfn_with_active_regions - Find the maximum PFN registered * * It returns the maximum PFN based on information provided via - * add_active_range() + * add_active_range(). */ unsigned long __init find_max_pfn_with_active_regions(void) { @@ -2662,10 +2664,7 @@ unsigned long __init find_max_pfn_with_active_regions(void) /** * free_area_init_nodes - Initialise all pg_data_t and zone data - * @arch_max_dma_pfn: The maximum PFN usable for ZONE_DMA - * @arch_max_dma32_pfn: The maximum PFN usable for ZONE_DMA32 - * @arch_max_low_pfn: The maximum PFN usable for ZONE_NORMAL - * @arch_max_high_pfn: The maximum PFN usable for ZONE_HIGHMEM + * @max_zone_pfn: an array of max PFNs for each zone * * This will call free_area_init_node() for each active node in the system. * Using the page ranges provided by add_active_range(), the size of each @@ -2723,14 +2722,15 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn) #endif /* CONFIG_ARCH_POPULATES_NODE_MAP */ /** - * set_dma_reserve - Account the specified number of pages reserved in ZONE_DMA - * @new_dma_reserve - The number of pages to mark reserved + * set_dma_reserve - set the specified number of pages reserved in the first zone + * @new_dma_reserve: The number of pages to mark reserved * * The per-cpu batchsize and zone watermarks are determined by present_pages. * In the DMA zone, a significant percentage may be consumed by kernel image * and other unfreeable allocations which can skew the watermarks badly. This - * function may optionally be used to account for unfreeable pages in - * ZONE_DMA. The effect will be lower watermarks and smaller per-cpu batchsize + * function may optionally be used to account for unfreeable pages in the + * first zone (e.g., ZONE_DMA). The effect will be lower watermarks and + * smaller per-cpu batchsize. */ void __init set_dma_reserve(unsigned long new_dma_reserve) { @@ -2843,10 +2843,11 @@ static void setup_per_zone_lowmem_reserve(void) calculate_totalreserve_pages(); } -/* - * setup_per_zone_pages_min - called when min_free_kbytes changes. Ensures - * that the pages_{min,low,high} values for each zone are set correctly - * with respect to min_free_kbytes. +/** + * setup_per_zone_pages_min - called when min_free_kbytes changes. + * + * Ensures that the pages_{min,low,high} values for each zone are set correctly + * with respect to min_free_kbytes. */ void setup_per_zone_pages_min(void) { diff --git a/mm/slab.c b/mm/slab.c index 3dbd6f4e74..c23b99250d 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3488,22 +3488,25 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, } +#ifdef CONFIG_DEBUG_SLAB void *__kmalloc(size_t size, gfp_t flags) { -#ifndef CONFIG_DEBUG_SLAB - return __do_kmalloc(size, flags, NULL); -#else return __do_kmalloc(size, flags, __builtin_return_address(0)); -#endif } EXPORT_SYMBOL(__kmalloc); -#ifdef CONFIG_DEBUG_SLAB void *__kmalloc_track_caller(size_t size, gfp_t flags, void *caller) { return __do_kmalloc(size, flags, caller); } EXPORT_SYMBOL(__kmalloc_track_caller); + +#else +void *__kmalloc(size_t size, gfp_t flags) +{ + return __do_kmalloc(size, flags, NULL); +} +EXPORT_SYMBOL(__kmalloc); #endif /** diff --git a/mm/util.c b/mm/util.c index e14fa84ef3..ace2aea69f 100644 --- a/mm/util.c +++ b/mm/util.c @@ -11,7 +11,7 @@ */ void *__kzalloc(size_t size, gfp_t flags) { - void *ret = ____kmalloc(size, flags); + void *ret = kmalloc_track_caller(size, flags); if (ret) memset(ret, 0, size); return ret; @@ -33,7 +33,7 @@ char *kstrdup(const char *s, gfp_t gfp) return NULL; len = strlen(s) + 1; - buf = ____kmalloc(len, gfp); + buf = kmalloc_track_caller(len, gfp); if (buf) memcpy(buf, s, len); return buf; @@ -51,7 +51,7 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp) { void *p; - p = ____kmalloc(len, gfp); + p = kmalloc_track_caller(len, gfp); if (p) memcpy(p, src, len); return p; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index c448c7f6fd..3c23760c58 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -156,7 +156,8 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, /* Get the DATA. Size must match skb_add_mtu(). */ size = SKB_DATA_ALIGN(size); - data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + data = kmalloc_track_caller(size + sizeof(struct skb_shared_info), + gfp_mask); if (!data) goto nodata; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 638c0b5762..447d9aef46 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -903,9 +903,9 @@ out_seq: struct gss_svc_data { /* decoded gss client cred: */ struct rpc_gss_wire_cred clcred; - /* pointer to the beginning of the procedure-specific results, - * which may be encrypted/checksummed in svcauth_gss_release: */ - __be32 *body_start; + /* save a pointer to the beginning of the encoded verifier, + * for use in encryption/checksumming in svcauth_gss_release: */ + __be32 *verf_start; struct rsc *rsci; }; @@ -968,7 +968,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) if (!svcdata) goto auth_err; rqstp->rq_auth_data = svcdata; - svcdata->body_start = NULL; + svcdata->verf_start = NULL; svcdata->rsci = NULL; gc = &svcdata->clcred; @@ -1097,6 +1097,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) goto complete; case RPC_GSS_PROC_DATA: *authp = rpcsec_gsserr_ctxproblem; + svcdata->verf_start = resv->iov_base + resv->iov_len; if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) goto auth_err; rqstp->rq_cred = rsci->cred; @@ -1110,7 +1111,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) gc->gc_seq, rsci->mechctx)) goto auth_err; /* placeholders for length and seq. number: */ - svcdata->body_start = resv->iov_base + resv->iov_len; svc_putnl(resv, 0); svc_putnl(resv, 0); break; @@ -1119,7 +1119,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) gc->gc_seq, rsci->mechctx)) goto auth_err; /* placeholders for length and seq. number: */ - svcdata->body_start = resv->iov_base + resv->iov_len; svc_putnl(resv, 0); svc_putnl(resv, 0); break; @@ -1147,6 +1146,32 @@ out: return ret; } +u32 * +svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd) +{ + u32 *p, verf_len; + + p = gsd->verf_start; + gsd->verf_start = NULL; + + /* If the reply stat is nonzero, don't wrap: */ + if (*(p-1) != rpc_success) + return NULL; + /* Skip the verifier: */ + p += 1; + verf_len = ntohl(*p++); + p += XDR_QUADLEN(verf_len); + /* move accept_stat to right place: */ + memcpy(p, p + 2, 4); + /* Also don't wrap if the accept stat is nonzero: */ + if (*p != rpc_success) { + resbuf->head[0].iov_len -= 2 * 4; + return NULL; + } + p++; + return p; +} + static inline int svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) { @@ -1160,17 +1185,9 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) int integ_offset, integ_len; int stat = -EINVAL; - p = gsd->body_start; - gsd->body_start = NULL; - /* move accept_stat to right place: */ - memcpy(p, p + 2, 4); - /* Don't wrap in failure case: */ - /* Counting on not getting here if call was not even accepted! */ - if (*p != rpc_success) { - resbuf->head[0].iov_len -= 2 * 4; + p = svcauth_gss_prepare_to_wrap(resbuf, gsd); + if (p == NULL) goto out; - } - p++; integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; integ_len = resbuf->len - integ_offset; BUG_ON(integ_len % 4); @@ -1191,7 +1208,6 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) resbuf->tail[0].iov_base = resbuf->head[0].iov_base + resbuf->head[0].iov_len; resbuf->tail[0].iov_len = 0; - rqstp->rq_restailpage = 0; resv = &resbuf->tail[0]; } else { resv = &resbuf->tail[0]; @@ -1223,24 +1239,16 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) int offset; int pad; - p = gsd->body_start; - gsd->body_start = NULL; - /* move accept_stat to right place: */ - memcpy(p, p + 2, 4); - /* Don't wrap in failure case: */ - /* Counting on not getting here if call was not even accepted! */ - if (*p != rpc_success) { - resbuf->head[0].iov_len -= 2 * 4; + p = svcauth_gss_prepare_to_wrap(resbuf, gsd); + if (p == NULL) return 0; - } - p++; len = p++; offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; *p++ = htonl(gc->gc_seq); inpages = resbuf->pages; /* XXX: Would be better to write some xdr helper functions for * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ - if (resbuf->tail[0].iov_base && rqstp->rq_restailpage == 0) { + if (resbuf->tail[0].iov_base) { BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base + PAGE_SIZE); BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base); @@ -1258,7 +1266,6 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) resbuf->tail[0].iov_base = resbuf->head[0].iov_base + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; resbuf->tail[0].iov_len = 0; - rqstp->rq_restailpage = 0; } if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) return -ENOMEM; @@ -1282,7 +1289,7 @@ svcauth_gss_release(struct svc_rqst *rqstp) if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; /* Release can be called twice, but we only wrap once. */ - if (gsd->body_start == NULL) + if (gsd->verf_start == NULL) goto out; /* normally not set till svc_send, but we need it here: */ /* XXX: what for? Do we mess it up the moment we call svc_putu32 diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index a99e67b164..c2c8bb20d0 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -417,18 +417,15 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) if (size > RPCSVC_MAXPAYLOAD) size = RPCSVC_MAXPAYLOAD; pages = 2 + (size+ PAGE_SIZE -1) / PAGE_SIZE; - rqstp->rq_argused = 0; - rqstp->rq_resused = 0; arghi = 0; BUG_ON(pages > RPCSVC_MAXPAGES); while (pages) { struct page *p = alloc_page(GFP_KERNEL); if (!p) break; - rqstp->rq_argpages[arghi++] = p; + rqstp->rq_pages[arghi++] = p; pages--; } - rqstp->rq_arghi = arghi; return ! pages; } @@ -438,14 +435,10 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) static void svc_release_buffer(struct svc_rqst *rqstp) { - while (rqstp->rq_arghi) - put_page(rqstp->rq_argpages[--rqstp->rq_arghi]); - while (rqstp->rq_resused) { - if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) - continue; - put_page(rqstp->rq_respages[rqstp->rq_resused]); - } - rqstp->rq_argused = 0; + int i; + for (i=0; irq_pages); i++) + if (rqstp->rq_pages[i]) + put_page(rqstp->rq_pages[i]); } /* @@ -651,23 +644,32 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) unsigned long flags; int i, error = 0, dummy; - progp = serv->sv_program; - - dprintk("RPC: svc_register(%s, %s, %d)\n", - progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port); - if (!port) clear_thread_flag(TIF_SIGPENDING); - for (i = 0; i < progp->pg_nvers; i++) { - if (progp->pg_vers[i] == NULL) - continue; - error = rpc_register(progp->pg_prog, i, proto, port, &dummy); - if (error < 0) - break; - if (port && !dummy) { - error = -EACCES; - break; + for (progp = serv->sv_program; progp; progp = progp->pg_next) { + for (i = 0; i < progp->pg_nvers; i++) { + if (progp->pg_vers[i] == NULL) + continue; + + dprintk("RPC: svc_register(%s, %s, %d, %d)%s\n", + progp->pg_name, + proto == IPPROTO_UDP? "udp" : "tcp", + port, + i, + progp->pg_vers[i]->vs_hidden? + " (but not telling portmap)" : ""); + + if (progp->pg_vers[i]->vs_hidden) + continue; + + error = rpc_register(progp->pg_prog, i, proto, port, &dummy); + if (error < 0) + break; + if (port && !dummy) { + error = -EACCES; + break; + } } } @@ -697,7 +699,7 @@ svc_process(struct svc_rqst *rqstp) u32 dir, prog, vers, proc; __be32 auth_stat, rpc_stat; int auth_res; - __be32 *accept_statp; + __be32 *reply_statp; rpc_stat = rpc_success; @@ -707,10 +709,10 @@ svc_process(struct svc_rqst *rqstp) /* setup response xdr_buf. * Initially it has just one page */ - svc_take_page(rqstp); /* must succeed */ + rqstp->rq_resused = 1; resv->iov_base = page_address(rqstp->rq_respages[0]); resv->iov_len = 0; - rqstp->rq_res.pages = rqstp->rq_respages+1; + rqstp->rq_res.pages = rqstp->rq_respages + 1; rqstp->rq_res.len = 0; rqstp->rq_res.page_base = 0; rqstp->rq_res.page_len = 0; @@ -738,7 +740,7 @@ svc_process(struct svc_rqst *rqstp) goto err_bad_rpc; /* Save position in case we later decide to reject: */ - accept_statp = resv->iov_base + resv->iov_len; + reply_statp = resv->iov_base + resv->iov_len; svc_putnl(resv, 0); /* ACCEPT */ @@ -886,7 +888,7 @@ err_bad_auth: dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat)); serv->sv_stats->rpcbadauth++; /* Restore write pointer to location of accept status: */ - xdr_ressize_check(rqstp, accept_statp); + xdr_ressize_check(rqstp, reply_statp); svc_putnl(resv, 1); /* REJECT */ svc_putnl(resv, 1); /* AUTH_ERROR */ svc_putnl(resv, ntohl(auth_stat)); /* status */ @@ -926,3 +928,18 @@ err_bad: svc_putnl(resv, ntohl(rpc_stat)); goto sendit; } + +/* + * Return (transport-specific) limit on the rpc payload. + */ +u32 svc_max_payload(const struct svc_rqst *rqstp) +{ + int max = RPCSVC_MAXPAYLOAD_TCP; + + if (rqstp->rq_sock->sk_sock->type == SOCK_DGRAM) + max = RPCSVC_MAXPAYLOAD_UDP; + if (rqstp->rq_server->sv_bufsz < max) + max = rqstp->rq_server->sv_bufsz; + return max; +} +EXPORT_SYMBOL_GPL(svc_max_payload); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 40d41a2831..e1bd933629 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -9,6 +9,7 @@ #include #include #include +#include #define RPCDBG_FACILITY RPCDBG_AUTH @@ -375,6 +376,44 @@ void svcauth_unix_purge(void) cache_purge(&ip_map_cache); } +static inline struct ip_map * +ip_map_cached_get(struct svc_rqst *rqstp) +{ + struct ip_map *ipm = rqstp->rq_sock->sk_info_authunix; + if (ipm != NULL) { + if (!cache_valid(&ipm->h)) { + /* + * The entry has been invalidated since it was + * remembered, e.g. by a second mount from the + * same IP address. + */ + rqstp->rq_sock->sk_info_authunix = NULL; + cache_put(&ipm->h, &ip_map_cache); + return NULL; + } + cache_get(&ipm->h); + } + return ipm; +} + +static inline void +ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm) +{ + struct svc_sock *svsk = rqstp->rq_sock; + + if (svsk->sk_sock->type == SOCK_STREAM && svsk->sk_info_authunix == NULL) + svsk->sk_info_authunix = ipm; /* newly cached, keep the reference */ + else + cache_put(&ipm->h, &ip_map_cache); +} + +void +svcauth_unix_info_release(void *info) +{ + struct ip_map *ipm = info; + cache_put(&ipm->h, &ip_map_cache); +} + static int svcauth_unix_set_client(struct svc_rqst *rqstp) { @@ -384,8 +423,10 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) if (rqstp->rq_proc == 0) return SVC_OK; - ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, - rqstp->rq_addr.sin_addr); + ipm = ip_map_cached_get(rqstp); + if (ipm == NULL) + ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, + rqstp->rq_addr.sin_addr); if (ipm == NULL) return SVC_DENIED; @@ -400,7 +441,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) case 0: rqstp->rq_client = &ipm->m_client->h; kref_get(&rqstp->rq_client->ref); - cache_put(&ipm->h, &ip_map_cache); + ip_map_cached_put(rqstp, ipm); break; } return SVC_OK; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index cba85d1952..b39e7e2b64 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -313,7 +313,7 @@ svc_sock_release(struct svc_rqst *rqstp) svc_release_skb(rqstp); - svc_free_allpages(rqstp); + svc_free_res_pages(rqstp); rqstp->rq_res.page_len = 0; rqstp->rq_res.page_base = 0; @@ -412,7 +412,8 @@ svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) /* send head */ if (slen == xdr->head[0].iov_len) flags = 0; - len = kernel_sendpage(sock, rqstp->rq_respages[0], 0, xdr->head[0].iov_len, flags); + len = kernel_sendpage(sock, rqstp->rq_respages[0], 0, + xdr->head[0].iov_len, flags); if (len != xdr->head[0].iov_len) goto out; slen -= xdr->head[0].iov_len; @@ -437,8 +438,9 @@ svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) } /* send tail */ if (xdr->tail[0].iov_len) { - result = kernel_sendpage(sock, rqstp->rq_respages[rqstp->rq_restailpage], - ((unsigned long)xdr->tail[0].iov_base)& (PAGE_SIZE-1), + result = kernel_sendpage(sock, rqstp->rq_respages[0], + ((unsigned long)xdr->tail[0].iov_base) + & (PAGE_SIZE-1), xdr->tail[0].iov_len, 0); if (result > 0) @@ -492,7 +494,12 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose) } spin_unlock(&serv->sv_lock); if (closesk) + /* Should unregister with portmap, but you cannot + * unregister just one protocol... + */ svc_delete_socket(closesk); + else if (toclose) + return -ENOENT; return len; } EXPORT_SYMBOL(svc_sock_names); @@ -703,9 +710,11 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) if (len <= rqstp->rq_arg.head[0].iov_len) { rqstp->rq_arg.head[0].iov_len = len; rqstp->rq_arg.page_len = 0; + rqstp->rq_respages = rqstp->rq_pages+1; } else { rqstp->rq_arg.page_len = len - rqstp->rq_arg.head[0].iov_len; - rqstp->rq_argused += (rqstp->rq_arg.page_len + PAGE_SIZE - 1)/ PAGE_SIZE; + rqstp->rq_respages = rqstp->rq_pages + 1 + + (rqstp->rq_arg.page_len + PAGE_SIZE - 1)/ PAGE_SIZE; } if (serv->sv_stats) @@ -946,7 +955,7 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) struct svc_sock *svsk = rqstp->rq_sock; struct svc_serv *serv = svsk->sk_server; int len; - struct kvec vec[RPCSVC_MAXPAGES]; + struct kvec *vec; int pnum, vlen; dprintk("svc: tcp_recv %p data %d conn %d close %d\n", @@ -1044,15 +1053,17 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) len = svsk->sk_reclen; set_bit(SK_DATA, &svsk->sk_flags); + vec = rqstp->rq_vec; vec[0] = rqstp->rq_arg.head[0]; vlen = PAGE_SIZE; pnum = 1; while (vlen < len) { - vec[pnum].iov_base = page_address(rqstp->rq_argpages[rqstp->rq_argused++]); + vec[pnum].iov_base = page_address(rqstp->rq_pages[pnum]); vec[pnum].iov_len = PAGE_SIZE; pnum++; vlen += PAGE_SIZE; } + rqstp->rq_respages = &rqstp->rq_pages[pnum]; /* Now receive data */ len = svc_recvfrom(rqstp, vec, pnum, len); @@ -1204,7 +1215,7 @@ svc_recv(struct svc_rqst *rqstp, long timeout) struct svc_sock *svsk =NULL; struct svc_serv *serv = rqstp->rq_server; struct svc_pool *pool = rqstp->rq_pool; - int len; + int len, i; int pages; struct xdr_buf *arg; DECLARE_WAITQUEUE(wait, current); @@ -1221,27 +1232,22 @@ svc_recv(struct svc_rqst *rqstp, long timeout) "svc_recv: service %p, wait queue active!\n", rqstp); - /* Initialize the buffers */ - /* first reclaim pages that were moved to response list */ - svc_pushback_allpages(rqstp); /* now allocate needed pages. If we get a failure, sleep briefly */ pages = 2 + (serv->sv_bufsz + PAGE_SIZE -1) / PAGE_SIZE; - while (rqstp->rq_arghi < pages) { - struct page *p = alloc_page(GFP_KERNEL); - if (!p) { - schedule_timeout_uninterruptible(msecs_to_jiffies(500)); - continue; + for (i=0; i < pages ; i++) + while (rqstp->rq_pages[i] == NULL) { + struct page *p = alloc_page(GFP_KERNEL); + if (!p) + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + rqstp->rq_pages[i] = p; } - rqstp->rq_argpages[rqstp->rq_arghi++] = p; - } /* Make arg->head point to first page and arg->pages point to rest */ arg = &rqstp->rq_arg; - arg->head[0].iov_base = page_address(rqstp->rq_argpages[0]); + arg->head[0].iov_base = page_address(rqstp->rq_pages[0]); arg->head[0].iov_len = PAGE_SIZE; - rqstp->rq_argused = 1; - arg->pages = rqstp->rq_argpages + 1; + arg->pages = rqstp->rq_pages + 1; arg->page_base = 0; /* save at least one page for response */ arg->page_len = (pages-2)*PAGE_SIZE; @@ -1604,6 +1610,8 @@ svc_delete_socket(struct svc_sock *svsk) sockfd_put(svsk->sk_sock); else sock_release(svsk->sk_sock); + if (svsk->sk_info_authunix != NULL) + svcauth_unix_info_release(svsk->sk_info_authunix); kfree(svsk); } else { spin_unlock_bh(&serv->sv_lock); @@ -1699,6 +1707,7 @@ static int svc_deferred_recv(struct svc_rqst *rqstp) rqstp->rq_prot = dr->prot; rqstp->rq_addr = dr->addr; rqstp->rq_daddr = dr->daddr; + rqstp->rq_respages = rqstp->rq_pages; return dr->argslen<<2; } diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst index cac8f21a33..6a026f69b5 100644 --- a/scripts/Makefile.headersinst +++ b/scripts/Makefile.headersinst @@ -97,7 +97,7 @@ quiet_cmd_unifdef = UNIFDEF $(patsubst $(INSTALL_HDR_PATH)/%,%,$@) | $(HDRSED) > $@ || : quiet_cmd_check = CHECK $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/.check.%,$(_dst)/%,$@) - cmd_check = $(srctree)/scripts/hdrcheck.sh \ + cmd_check = $(CONFIG_SHELL) $(srctree)/scripts/hdrcheck.sh \ $(INSTALL_HDR_PATH)/include $(subst /.check.,/,$@) $@ quiet_cmd_remove = REMOVE $(_dst)/$@ diff --git a/sound/oss/Makefile b/sound/oss/Makefile index 8681179200..2489bd6bb0 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -15,71 +15,42 @@ obj-$(CONFIG_SOUND_HAL2) += hal2.o 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_SSCAPE) += sscape.o ad1848.o mpu401.o -obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o obj-$(CONFIG_SOUND_MSS) += ad1848.o obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o -obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o -obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o obj-$(CONFIG_SOUND_MPU401) += mpu401.o obj-$(CONFIG_SOUND_UART6850) += uart6850.o -obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o obj-$(CONFIG_SOUND_YM3812) += opl3.o obj-$(CONFIG_SOUND_VMIDI) += v_midi.o obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o -obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o obj-$(CONFIG_SOUND_AD1816) += ad1816.o obj-$(CONFIG_SOUND_AD1889) += ad1889.o ac97_codec.o obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o -obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o ifeq ($(CONFIG_MIDI_VIA82CXXX),y) obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o endif -obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o -ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y) - obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o -endif obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o obj-$(CONFIG_SOUND_VWSND) += vwsnd.o obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o -obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o -obj-$(CONFIG_SOUND_CMPCI) += cmpci.o -ifeq ($(CONFIG_SOUND_CMPCI_FM),y) - obj-$(CONFIG_SOUND_CMPCI) += sound.o opl3.o -endif -ifeq ($(CONFIG_SOUND_CMPCI_MIDI),y) - obj-$(CONFIG_SOUND_CMPCI) += sound.o mpu401.o -endif -obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o -obj-$(CONFIG_SOUND_AU1000) += au1000.o ac97_codec.o obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o -obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o -obj-$(CONFIG_SOUND_MAESTRO) += maestro.o -obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o -obj-$(CONFIG_SOUND_HARMONY) += harmony.o obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o -obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o obj-$(CONFIG_SOUND_BT878) += btaudio.o -obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o -obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o -obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o ac97_codec.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o ifeq ($(CONFIG_MIDI_EMU10K1),y) @@ -87,28 +58,25 @@ ifeq ($(CONFIG_MIDI_EMU10K1),y) endif obj-$(CONFIG_SOUND_EMU10K1) += emu10k1/ -obj-$(CONFIG_SOUND_CS4281) += cs4281/ obj-$(CONFIG_DMASOUND) += dmasound/ # Declare multi-part drivers. sound-objs := \ - dev_table.o soundcard.o sound_syms.o \ - audio.o audio_syms.o dmabuf.o \ - midi_syms.o midi_synth.o midibuf.o \ - sequencer.o sequencer_syms.o sound_timer.o sys_timer.o + dev_table.o soundcard.o \ + audio.o dmabuf.o \ + midi_synth.o midibuf.o \ + sequencer.o sound_timer.o sys_timer.o -gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o sb-objs := sb_card.o sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o vidc_mod-objs := vidc.o vidc_fill.o -wavefront-objs := wavfront.o wf_midi.o yss225.o hostprogs-y := bin2hex hex2hex # Files generated that shall be removed upon make clean -clean-files := maui_boot.h msndperm.c msndinit.c pndsperm.c pndspini.c \ +clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \ pss_boot.h trix_boot.h # Firmware files that need translation @@ -118,21 +86,6 @@ clean-files := maui_boot.h msndperm.c msndinit.c pndsperm.c pndspini.c \ # will be forced to be remade. # -# Turtle Beach Maui / Tropez - -$(obj)/maui.o: $(obj)/maui_boot.h - -ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) - $(obj)/maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) $(obj)/bin2hex - $(obj)/bin2hex -i maui_os < $< > $@ -else - $(obj)/maui_boot.h: - ( \ - echo 'static unsigned char * maui_os = NULL;'; \ - echo 'static int maui_osLen = 0;'; \ - ) > $@ -endif - # Turtle Beach MultiSound ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y) diff --git a/sound/oss/ac97.c b/sound/oss/ac97.c index 3ba6d91e89..72cf4ed779 100644 --- a/sound/oss/ac97.c +++ b/sound/oss/ac97.c @@ -112,25 +112,6 @@ ac97_init (struct ac97_hwint *dev) return 0; } -/* Reset the mixer to the currently saved settings. */ -int -ac97_reset (struct ac97_hwint *dev) -{ - int x; - - if (dev->reset_device (dev)) - return -1; - - /* Now set the registers back to their last-written values. */ - for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { - int regnum = mixerRegs[x].ac97_regnum; - int value = dev->last_written_mixer_values [regnum / 2]; - if (value >= 0) - ac97_put_register (dev, regnum, value); - } - return 0; -} - /* Return the contents of register REG; use the cache if the value in it is valid. Returns a negative error code on failure. */ static int @@ -441,7 +422,6 @@ EXPORT_SYMBOL(ac97_init); EXPORT_SYMBOL(ac97_set_values); EXPORT_SYMBOL(ac97_put_register); EXPORT_SYMBOL(ac97_mixer_ioctl); -EXPORT_SYMBOL(ac97_reset); MODULE_LICENSE("GPL"); diff --git a/sound/oss/ac97.h b/sound/oss/ac97.h index 77d454ea32..01837a9d7d 100644 --- a/sound/oss/ac97.h +++ b/sound/oss/ac97.h @@ -192,9 +192,6 @@ extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value); extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, void __user * arg); -/* Do a complete reset on the AC97 mixer, restoring all mixer registers to - the current values. Normally used after an APM resume event. */ -extern int ac97_reset (struct ac97_hwint *dev); #endif /* diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c index 972327c976..602db49792 100644 --- a/sound/oss/ac97_codec.c +++ b/sound/oss/ac97_codec.c @@ -1399,95 +1399,6 @@ unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) EXPORT_SYMBOL(ac97_set_adc_rate); -int ac97_save_state(struct ac97_codec *codec) -{ - return 0; -} - -EXPORT_SYMBOL(ac97_save_state); - -int ac97_restore_state(struct ac97_codec *codec) -{ - int i; - unsigned int left, right, val; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!supported_mixer(codec, i)) - continue; - - val = codec->mixer_state[i]; - right = val >> 8; - left = val & 0xff; - codec->write_mixer(codec, i, left, right); - } - return 0; -} - -EXPORT_SYMBOL(ac97_restore_state); - -/** - * ac97_register_driver - register a codec helper - * @driver: Driver handler - * - * Register a handler for codecs matching the codec id. The handler - * attach function is called for all present codecs and will be - * called when new codecs are discovered. - */ - -int ac97_register_driver(struct ac97_driver *driver) -{ - struct list_head *l; - struct ac97_codec *c; - - mutex_lock(&codec_mutex); - INIT_LIST_HEAD(&driver->list); - list_add(&driver->list, &codec_drivers); - - list_for_each(l, &codecs) - { - c = list_entry(l, struct ac97_codec, list); - if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask)) - continue; - if(driver->probe(c, driver)) - continue; - c->driver = driver; - } - mutex_unlock(&codec_mutex); - return 0; -} - -EXPORT_SYMBOL_GPL(ac97_register_driver); - -/** - * ac97_unregister_driver - unregister a codec helper - * @driver: Driver handler - * - * Unregister a handler for codecs matching the codec id. The handler - * remove function is called for all matching codecs. - */ - -void ac97_unregister_driver(struct ac97_driver *driver) -{ - struct list_head *l; - struct ac97_codec *c; - - mutex_lock(&codec_mutex); - list_del_init(&driver->list); - - list_for_each(l, &codecs) - { - c = list_entry(l, struct ac97_codec, list); - if (c->driver == driver) { - driver->remove(c, driver); - c->driver = NULL; - } - } - - mutex_unlock(&codec_mutex); -} - -EXPORT_SYMBOL_GPL(ac97_unregister_driver); - static int swap_headphone(int remove_master) { struct list_head *l; diff --git a/sound/oss/ac97_plugin_ad1980.c b/sound/oss/ac97_plugin_ad1980.c deleted file mode 100644 index 24a9acd281..0000000000 --- a/sound/oss/ac97_plugin_ad1980.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - ac97_plugin_ad1980.c Copyright (C) 2003 Red Hat, Inc. All rights reserved. - - The contents of this file are subject to the Open Software License version 1.1 - that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is - included herein by reference. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL") as - distributed in the kernel source COPYING file, in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the OSL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the OSL or the GPL. - - Authors: Alan Cox - - This is an example codec plugin. This one switches the connections - around to match the setups some vendors use with audio switched to - non standard front connectors not the normal rear ones - - This code primarily exists to demonstrate how to use the codec - interface - -*/ - -#include -#include -#include -#include -#include - -/** - * ad1980_remove - codec remove callback - * @codec: The codec that is being removed - * - * This callback occurs when an AC97 codec is being removed. A - * codec remove call will not occur for a codec during that codec - * probe callback. - * - * Most drivers will need to lock their remove versus their - * use of the codec after the probe function. - */ - -static void __devexit ad1980_remove(struct ac97_codec *codec, struct ac97_driver *driver) -{ - /* Nothing to do in the simple example */ -} - - -/** - * ad1980_probe - codec found callback - * @codec: ac97 codec matching the idents - * @driver: ac97_driver it matched - * - * This entry point is called when a codec is found which matches - * the driver. At the point it is called the codec is basically - * operational, mixer operations have been initialised and can - * be overriden. Called in process context. The field driver_private - * is available for the driver to use to store stuff. - * - * The caller can claim the device by returning zero, or return - * a negative error code. - */ - -static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver) -{ - u16 control; - -#define AC97_AD_MISC 0x76 - - /* Switch the inputs/outputs over (from Dell code) */ - control = codec->codec_read(codec, AC97_AD_MISC); - codec->codec_write(codec, AC97_AD_MISC, control | 0x4420); - - /* We could refuse the device since we dont need to hang around, - but we will claim it */ - return 0; -} - - -static struct ac97_driver ad1980_driver = { - .codec_id = 0x41445370, - .codec_mask = 0xFFFFFFFF, - .name = "AD1980 example", - .probe = ad1980_probe, - .remove = __devexit_p(ad1980_remove), -}; - -/** - * ad1980_exit - module exit path - * - * Our module is being unloaded. At this point unregister_driver - * will call back our remove handler for any existing codecs. You - * may not unregister_driver from interrupt context or from a - * probe/remove callback. - */ - -static void ad1980_exit(void) -{ - ac97_unregister_driver(&ad1980_driver); -} - -/** - * ad1980_init - set up ad1980 handlers - * - * After we call the register function it will call our probe - * function for each existing matching device before returning to us. - * Any devices appearing afterwards whose id's match the codec_id - * will also cause the probe function to be called. - * You may not register_driver from interrupt context or from a - * probe/remove callback. - */ - -static int ad1980_init(void) -{ - return ac97_register_driver(&ad1980_driver); -} - -module_init(ad1980_init); -module_exit(ad1980_exit); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index f6b6b886c2..257b7536fb 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -195,6 +195,7 @@ static void ad1848_halt(int dev); static void ad1848_halt_input(int dev); static void ad1848_halt_output(int dev); static void ad1848_trigger(int dev, int bits); +static irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy); #ifndef EXCLUDE_TIMERS static int ad1848_tmr_install(int dev); @@ -2195,7 +2196,7 @@ void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); } -irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy) +static irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy) { unsigned char status; ad1848_info *devc; @@ -2802,7 +2803,6 @@ EXPORT_SYMBOL(ad1848_detect); EXPORT_SYMBOL(ad1848_init); EXPORT_SYMBOL(ad1848_unload); EXPORT_SYMBOL(ad1848_control); -EXPORT_SYMBOL(adintr); EXPORT_SYMBOL(probe_ms_sound); EXPORT_SYMBOL(attach_ms_sound); EXPORT_SYMBOL(unload_ms_sound); diff --git a/sound/oss/ad1848.h b/sound/oss/ad1848.h index d0573b0239..b95ebe28d4 100644 --- a/sound/oss/ad1848.h +++ b/sound/oss/ad1848.h @@ -18,7 +18,6 @@ void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int int ad1848_detect (struct resource *ports, int *flags, int *osp); int ad1848_control(int cmd, int arg); -irqreturn_t adintr(int irq, void *dev_id, struct pt_regs * dummy); void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner); int probe_ms_sound(struct address_info *hw_config, struct resource *ports); diff --git a/sound/oss/ali5455.c b/sound/oss/ali5455.c deleted file mode 100644 index 70dcd703a6..0000000000 --- a/sound/oss/ali5455.c +++ /dev/null @@ -1,3735 +0,0 @@ -/* - * ALI ali5455 and friends ICH driver for Linux - * LEI HU - * - * Built from: - * drivers/sound/i810_audio - * - * The ALi 5455 is similar but not quite identical to the Intel ICH - * series of controllers. Its easier to keep the driver separated from - * the i810 driver. - * - * 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. - * - * - * ALi 5455 theory of operation - * - * The chipset provides three DMA channels that talk to an AC97 - * CODEC (AC97 is a digital/analog mixer standard). At its simplest - * you get 48Khz audio with basic volume and mixer controls. At the - * best you get rate adaption in the codec. We set the card up so - * that we never take completion interrupts but instead keep the card - * chasing its tail around a ring buffer. This is needed for mmap - * mode audio and happens to work rather well for non-mmap modes too. - * - * The board has one output channel for PCM audio (supported) and - * a stereo line in and mono microphone input. Again these are normally - * locked to 48Khz only. Right now recording is not finished. - * - * There is no midi support, no synth support. Use timidity. To get - * esd working you need to use esd -r 48000 as it won't probe 48KHz - * by default. mpg123 can't handle 48Khz only audio so use xmms. - * - * If you need to force a specific rate set the clocking= option - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef PCI_DEVICE_ID_ALI_5455 -#define PCI_DEVICE_ID_ALI_5455 0x5455 -#endif - -#ifndef PCI_VENDOR_ID_ALI -#define PCI_VENDOR_ID_ALI 0x10b9 -#endif - -static int strict_clocking = 0; -static unsigned int clocking = 0; -static unsigned int codec_pcmout_share_spdif_locked = 0; -static unsigned int codec_independent_spdif_locked = 0; -static unsigned int controller_pcmout_share_spdif_locked = 0; -static unsigned int controller_independent_spdif_locked = 0; -static unsigned int globel = 0; - -#define ADC_RUNNING 1 -#define DAC_RUNNING 2 -#define CODEC_SPDIFOUT_RUNNING 8 -#define CONTROLLER_SPDIFOUT_RUNNING 4 - -#define SPDIF_ENABLE_OUTPUT 4 /* bits 0,1 are PCM */ - -#define ALI5455_FMT_16BIT 1 -#define ALI5455_FMT_STEREO 2 -#define ALI5455_FMT_MASK 3 - -#define SPDIF_ON 0x0004 -#define SURR_ON 0x0010 -#define CENTER_LFE_ON 0x0020 -#define VOL_MUTED 0x8000 - - -#define ALI_SPDIF_OUT_CH_STATUS 0xbf -/* the 810's array of pointers to data buffers */ - -struct sg_item { -#define BUSADDR_MASK 0xFFFFFFFE - u32 busaddr; -#define CON_IOC 0x80000000 /* interrupt on completion */ -#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */ -#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ - u32 control; -}; - -/* an instance of the ali channel */ -#define SG_LEN 32 -struct ali_channel { - /* these sg guys should probably be allocated - separately as nocache. Must be 8 byte aligned */ - struct sg_item sg[SG_LEN]; /* 32*8 */ - u32 offset; /* 4 */ - u32 port; /* 4 */ - u32 used; - u32 num; -}; - -/* - * we have 3 separate dma engines. pcm in, pcm out, and mic. - * each dma engine has controlling registers. These goofy - * names are from the datasheet, but make it easy to write - * code while leafing through it. - */ - -#define ENUM_ENGINE(PRE,DIG) \ -enum { \ - PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ - PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ - PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ - PRE##_SR = 0x##DIG##6, /* Status Register */ \ - PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ - PRE##_CR = 0x##DIG##b /* Control Register */ \ -} - -ENUM_ENGINE(OFF, 0); /* Offsets */ -ENUM_ENGINE(PI, 4); /* PCM In */ -ENUM_ENGINE(PO, 5); /* PCM Out */ -ENUM_ENGINE(MC, 6); /* Mic In */ -ENUM_ENGINE(CODECSPDIFOUT, 7); /* CODEC SPDIF OUT */ -ENUM_ENGINE(CONTROLLERSPDIFIN, A); /* CONTROLLER SPDIF In */ -ENUM_ENGINE(CONTROLLERSPDIFOUT, B); /* CONTROLLER SPDIF OUT */ - - -enum { - ALI_SCR = 0x00, /* System Control Register */ - ALI_SSR = 0x04, /* System Status Register */ - ALI_DMACR = 0x08, /* DMA Control Register */ - ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ - ALI_INTERFACECR = 0x10, /* Interface Control Register */ - ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ - ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ - ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ - ALI_CPR = 0x20, /* Command Port Register */ - ALI_SPR = 0x24, /* Status Port Register */ - ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ - ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ - ALI_RTSR = 0x34, /* Receive Tag Slot Register */ - ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ - ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ - ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ - ALI_SPDIFICS = 0xfc /* spdif interface control/status */ -}; - -// x-status register(x:pcm in ,pcm out, mic in,) -/* interrupts for a dma engine */ -#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ -#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ -#define DMA_INT_LVI (1<<2) /* last valid done */ -#define DMA_INT_CELV (1<<1) /* last valid is current */ -#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ //not eqult intel -#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) - -/* interrupts for the whole chip */// by interrupt status register finish - -#define INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */ -#define INT_SPDIFIN (1<<22) -#define INT_CODECSPDIFOUT (1<<19) -#define INT_MICIN (1<<18) -#define INT_PCMOUT (1<<17) -#define INT_PCMIN (1<<16) -#define INT_CPRAIS (1<<7) -#define INT_SPRAIS (1<<5) -#define INT_GPIO (1<<1) -#define INT_MASK (INT_SPDIFOUT|INT_CODECSPDIFOUT|INT_MICIN|INT_PCMOUT|INT_PCMIN) - -#define DRIVER_VERSION "0.02ac" - -/* magic numbers to protect our data structures */ -#define ALI5455_CARD_MAGIC 0x5072696E /* "Prin" */ -#define ALI5455_STATE_MAGIC 0x63657373 /* "cess" */ -#define ALI5455_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ -#define NR_HW_CH 5 //I think 5 channel - -/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define NR_AC97 2 - -/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */ -/* stream at a minimum for this card to be happy */ -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */ -/* values are one less than might be expected */ -static const unsigned sample_shift[] = { -1, 0, 0, 1 }; - -#define ALI5455 -static char *card_names[] = { - "ALI 5455" -}; - -static struct pci_device_id ali_pci_tbl[] = { - {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5455, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI5455}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, ali_pci_tbl); - -#ifdef CONFIG_PM -#define PM_SUSPENDED(card) (card->pm_suspended) -#else -#define PM_SUSPENDED(card) (0) -#endif - -/* "software" or virtual channel, an instance of opened /dev/dsp */ -struct ali_state { - unsigned int magic; - struct ali_card *card; /* Card info */ - - /* single open lock mechanism, only used for recording */ - struct mutex open_mutex; - wait_queue_head_t open_wait; - - /* file mode */ - mode_t open_mode; - - /* virtual channel number */ - int virt; - -#ifdef CONFIG_PM - unsigned int pm_saved_dac_rate, pm_saved_adc_rate; -#endif - struct dmabuf { - /* wave sample stuff */ - unsigned int rate; - unsigned char fmt, enable, trigger; - - /* hardware channel */ - struct ali_channel *read_channel; - struct ali_channel *write_channel; - struct ali_channel *codec_spdifout_channel; - struct ali_channel *controller_spdifout_channel; - - /* OSS buffer management stuff */ - void *rawbuf; - dma_addr_t dma_handle; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - - /* our buffer acts like a circular ring */ - unsigned hwptr; /* where dma last started, updated by update_ptr */ - unsigned swptr; /* where driver last clear/filled, updated by read/write */ - int count; /* bytes to be consumed or been generated by dma machine */ - unsigned total_bytes; /* total bytes dmaed by hardware */ - - unsigned error; /* number of over/underruns */ - wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ - - /* redundant, but makes calculations easier */ - /* what the hardware uses */ - unsigned dmasize; - unsigned fragsize; - unsigned fragsamples; - - /* what we tell the user to expect */ - unsigned userfrags; - unsigned userfragsize; - - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned update_flag; - unsigned ossfragsize; - unsigned ossmaxfrags; - unsigned subdivision; - } dmabuf; -}; - - -struct ali_card { - struct ali_channel channel[5]; - unsigned int magic; - - /* We keep ali5455 cards in a linked list */ - struct ali_card *next; - - /* The ali has a certain amount of cross channel interaction - so we use a single per card lock */ - spinlock_t lock; - spinlock_t ac97_lock; - - /* PCI device stuff */ - struct pci_dev *pci_dev; - u16 pci_id; -#ifdef CONFIG_PM - u16 pm_suspended; - int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97]; -#endif - /* soundcore stuff */ - int dev_audio; - - /* structures for abstraction of hardware facilities, codecs, banks and channels */ - struct ac97_codec *ac97_codec[NR_AC97]; - struct ali_state *states[NR_HW_CH]; - - u16 ac97_features; - u16 ac97_status; - u16 channels; - - /* hardware resources */ - unsigned long iobase; - - u32 irq; - - /* Function support */ - struct ali_channel *(*alloc_pcm_channel) (struct ali_card *); - struct ali_channel *(*alloc_rec_pcm_channel) (struct ali_card *); - struct ali_channel *(*alloc_rec_mic_channel) (struct ali_card *); - struct ali_channel *(*alloc_codec_spdifout_channel) (struct ali_card *); - struct ali_channel *(*alloc_controller_spdifout_channel) (struct ali_card *); - void (*free_pcm_channel) (struct ali_card *, int chan); - - /* We have a *very* long init time possibly, so use this to block */ - /* attempts to open our devices before we are ready (stops oops'es) */ - int initializing; -}; - - -static struct ali_card *devs = NULL; - -static int ali_open_mixdev(struct inode *inode, struct file *file); -static int ali_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg); -static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); - -static struct ali_channel *ali_alloc_pcm_channel(struct ali_card *card) -{ - if (card->channel[1].used == 1) - return NULL; - card->channel[1].used = 1; - return &card->channel[1]; -} - -static struct ali_channel *ali_alloc_rec_pcm_channel(struct ali_card *card) -{ - if (card->channel[0].used == 1) - return NULL; - card->channel[0].used = 1; - return &card->channel[0]; -} - -static struct ali_channel *ali_alloc_rec_mic_channel(struct ali_card *card) -{ - if (card->channel[2].used == 1) - return NULL; - card->channel[2].used = 1; - return &card->channel[2]; -} - -static struct ali_channel *ali_alloc_codec_spdifout_channel(struct ali_card *card) -{ - if (card->channel[3].used == 1) - return NULL; - card->channel[3].used = 1; - return &card->channel[3]; -} - -static struct ali_channel *ali_alloc_controller_spdifout_channel(struct ali_card *card) -{ - if (card->channel[4].used == 1) - return NULL; - card->channel[4].used = 1; - return &card->channel[4]; -} -static void ali_free_pcm_channel(struct ali_card *card, int channel) -{ - card->channel[channel].used = 0; -} - - -//add support codec spdif out -static int ali_valid_spdif_rate(struct ac97_codec *codec, int rate) -{ - unsigned long id = 0L; - - id = (ali_ac97_get(codec, AC97_VENDOR_ID1) << 16); - id |= ali_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; - switch (id) { - case 0x41445361: /* AD1886 */ - if (rate == 48000) { - return 1; - } - break; - case 0x414c4720: /* ALC650 */ - if (rate == 48000) { - return 1; - } - break; - default: /* all other codecs, until we know otherwiae */ - if (rate == 48000 || rate == 44100 || rate == 32000) { - return 1; - } - break; - } - return (0); -} - -/* ali_set_spdif_output - * - * Configure the S/PDIF output transmitter. When we turn on - * S/PDIF, we turn off the analog output. This may not be - * the right thing to do. - * - * Assumptions: - * The DSP sample rate must already be set to a supported - * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. - */ -static void ali_set_spdif_output(struct ali_state *state, int slots, - int rate) -{ - int vol; - int aud_reg; - struct ac97_codec *codec = state->card->ac97_codec[0]; - - if (!(state->card->ac97_features & 4)) { - state->card->ac97_status &= ~SPDIF_ON; - } else { - if (slots == -1) { /* Turn off S/PDIF */ - aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); - ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - - /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ - if (!(state->card->ac97_status & VOL_MUTED)) { - aud_reg = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); - ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, - (aud_reg & ~VOL_MUTED)); - } - state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); - return; - } - - vol = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); - state->card->ac97_status = vol & VOL_MUTED; - - /* Set S/PDIF transmitter sample rate */ - aud_reg = ali_ac97_get(codec, AC97_SPDIF_CONTROL); - switch (rate) { - case 32000: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; - break; - case 44100: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; - break; - case 48000: - aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; - break; - default: - /* turn off S/PDIF */ - aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); - ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - state->card->ac97_status &= ~SPDIF_ON; - return; - } - - ali_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); - - aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); - aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_SPDIF; - ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); - - aud_reg = ali_ac97_get(codec, AC97_POWER_CONTROL); - aud_reg |= 0x0002; - ali_ac97_set(codec, AC97_POWER_CONTROL, aud_reg); - udelay(1); - - state->card->ac97_status |= SPDIF_ON; - - /* Check to make sure the configuration is valid */ - aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); - if (!(aud_reg & 0x0400)) { - /* turn off S/PDIF */ - ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); - state->card->ac97_status &= ~SPDIF_ON; - return; - } - if (codec_independent_spdif_locked > 0) { - aud_reg = ali_ac97_get(codec, 0x6a); - ali_ac97_set(codec, 0x6a, (aud_reg & 0xefff)); - } - /* Mute the analog output */ - /* Should this only mute the PCM volume??? */ - } -} - -/* ali_set_dac_channels - * - * Configure the codec's multi-channel DACs - * - * The logic is backwards. Setting the bit to 1 turns off the DAC. - * - * What about the ICH? We currently configure it using the - * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, - * does that imply that we want the ICH set to support - * these channels? - * - * TODO: - * vailidate that the codec really supports these DACs - * before turning them on. - */ -static void ali_set_dac_channels(struct ali_state *state, int channel) -{ - int aud_reg; - struct ac97_codec *codec = state->card->ac97_codec[0]; - - aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); - aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; - state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); - - switch (channel) { - case 2: /* always enabled */ - break; - case 4: - aud_reg &= ~AC97_EA_PRJ; - state->card->ac97_status |= SURR_ON; - break; - case 6: - aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); - state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; - break; - default: - break; - } - ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); - -} - -/* set playback sample rate */ -static unsigned int ali_set_dac_rate(struct ali_state *state, - unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - u32 new_rate; - struct ac97_codec *codec = state->card->ac97_codec[0]; - - if (!(state->card->ac97_features & 0x0001)) { - dmabuf->rate = clocking; - return clocking; - } - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - dmabuf->rate = rate; - - /* - * Adjust for misclocked crap - */ - - rate = (rate * clocking) / 48000; - - if (strict_clocking && rate < 8000) { - rate = 8000; - dmabuf->rate = (rate * 48000) / clocking; - } - - new_rate = ac97_set_dac_rate(codec, rate); - if (new_rate != rate) { - dmabuf->rate = (new_rate * 48000) / clocking; - } - rate = new_rate; - return dmabuf->rate; -} - -/* set recording sample rate */ -static unsigned int ali_set_adc_rate(struct ali_state *state, - unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - u32 new_rate; - struct ac97_codec *codec = state->card->ac97_codec[0]; - - if (!(state->card->ac97_features & 0x0001)) { - dmabuf->rate = clocking; - return clocking; - } - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - dmabuf->rate = rate; - - /* - * Adjust for misclocked crap - */ - - rate = (rate * clocking) / 48000; - if (strict_clocking && rate < 8000) { - rate = 8000; - dmabuf->rate = (rate * 48000) / clocking; - } - - new_rate = ac97_set_adc_rate(codec, rate); - - if (new_rate != rate) { - dmabuf->rate = (new_rate * 48000) / clocking; - rate = new_rate; - } - return dmabuf->rate; -} - -/* set codec independent spdifout sample rate */ -static unsigned int ali_set_codecspdifout_rate(struct ali_state *state, - unsigned int rate) -{ - struct dmabuf *dmabuf = &state->dmabuf; - - if (!(state->card->ac97_features & 0x0001)) { - dmabuf->rate = clocking; - return clocking; - } - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - dmabuf->rate = rate; - - return dmabuf->rate; -} - -/* set controller independent spdif out function sample rate */ -static void ali_set_spdifout_rate(struct ali_state *state, - unsigned int rate) -{ - unsigned char ch_st_sel; - unsigned short status_rate; - - switch (rate) { - case 44100: - status_rate = 0; - break; - case 32000: - status_rate = 0x300; - break; - case 48000: - default: - status_rate = 0x200; - break; - } - - ch_st_sel = inb(state->card->iobase + ALI_SPDIFICS) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out - - ch_st_sel |= 0x80; //select right - outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); - outb(status_rate | 0x20, (state->card->iobase + ALI_SPDIFCSR + 2)); - - ch_st_sel &= (~0x80); //select left - outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); - outw(status_rate | 0x10, (state->card->iobase + ALI_SPDIFCSR + 2)); -} - -/* get current playback/recording dma buffer pointer (byte offset from LBA), - called with spinlock held! */ - -static inline unsigned ali_get_dma_addr(struct ali_state *state, int rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned int civ, offset, port, port_picb; - unsigned int data; - - if (!dmabuf->enable) - return 0; - - if (rec == 1) - port = state->card->iobase + dmabuf->read_channel->port; - else if (rec == 2) - port = state->card->iobase + dmabuf->codec_spdifout_channel->port; - else if (rec == 3) - port = state->card->iobase + dmabuf->controller_spdifout_channel->port; - else - port = state->card->iobase + dmabuf->write_channel->port; - - port_picb = port + OFF_PICB; - - do { - civ = inb(port + OFF_CIV) & 31; - offset = inw(port_picb); - /* Must have a delay here! */ - if (offset == 0) - udelay(1); - - /* Reread both registers and make sure that that total - * offset from the first reading to the second is 0. - * There is an issue with SiS hardware where it will count - * picb down to 0, then update civ to the next value, - * then set the new picb to fragsize bytes. We can catch - * it between the civ update and the picb update, making - * it look as though we are 1 fragsize ahead of where we - * are. The next to we get the address though, it will - * be back in thdelay is more than long enough - * that we won't have to worry about the chip still being - * out of sync with reality ;-) - */ - } while (civ != (inb(port + OFF_CIV) & 31) || offset != inw(port_picb)); - - data = ((civ + 1) * dmabuf->fragsize - (2 * offset)) % dmabuf->dmasize; - if (inw(port_picb) == 0) - data -= 2048; - - return data; -} - -/* Stop recording (lock held) */ -static inline void __stop_adc(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct ali_card *card = state->card; - - dmabuf->enable &= ~ADC_RUNNING; - - outl((1 << 18) | (1 << 16), card->iobase + ALI_DMACR); - udelay(1); - - outb(0, card->iobase + PI_CR); - while (inb(card->iobase + PI_CR) != 0); - - // now clear any latent interrupt bits (like the halt bit) - outb(inb(card->iobase + PI_SR) | 0x001e, card->iobase + PI_SR); - outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMIN, card->iobase + ALI_INTERRUPTSR); -} - -static void stop_adc(struct ali_state *state) -{ - struct ali_card *card = state->card; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - __stop_adc(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -static inline void __start_adc(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - - if (dmabuf->count < dmabuf->dmasize && dmabuf->ready - && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) { - dmabuf->enable |= ADC_RUNNING; - outb((1 << 4) | (1 << 2), state->card->iobase + PI_CR); - if (state->card->channel[0].used == 1) - outl(1, state->card->iobase + ALI_DMACR); // DMA CONTROL REGISTRER - udelay(100); - if (state->card->channel[2].used == 1) - outl((1 << 2), state->card->iobase + ALI_DMACR); //DMA CONTROL REGISTER - udelay(100); - } -} - -static void start_adc(struct ali_state *state) -{ - struct ali_card *card = state->card; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - __start_adc(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -/* stop playback (lock held) */ -static inline void __stop_dac(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct ali_card *card = state->card; - - dmabuf->enable &= ~DAC_RUNNING; - outl(0x00020000, card->iobase + 0x08); - outb(0, card->iobase + PO_CR); - while (inb(card->iobase + PO_CR) != 0) - cpu_relax(); - - outb(inb(card->iobase + PO_SR) | 0x001e, card->iobase + PO_SR); - - outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMOUT, card->iobase + ALI_INTERRUPTSR); -} - -static void stop_dac(struct ali_state *state) -{ - struct ali_card *card = state->card; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - __stop_dac(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -static inline void __start_dac(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && - (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { - dmabuf->enable |= DAC_RUNNING; - outb((1 << 4) | (1 << 2), state->card->iobase + PO_CR); - outl((1 << 1), state->card->iobase + 0x08); //dma control register - } -} - -static void start_dac(struct ali_state *state) -{ - struct ali_card *card = state->card; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - __start_dac(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -/* stop codec and controller spdif out (lock held) */ -static inline void __stop_spdifout(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct ali_card *card = state->card; - - if (codec_independent_spdif_locked > 0) { - dmabuf->enable &= ~CODEC_SPDIFOUT_RUNNING; - outl((1 << 19), card->iobase + 0x08); - outb(0, card->iobase + CODECSPDIFOUT_CR); - - while (inb(card->iobase + CODECSPDIFOUT_CR) != 0) - cpu_relax(); - - outb(inb(card->iobase + CODECSPDIFOUT_SR) | 0x001e, card->iobase + CODECSPDIFOUT_SR); - outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_CODECSPDIFOUT, card->iobase + ALI_INTERRUPTSR); - } else { - if (controller_independent_spdif_locked > 0) { - dmabuf->enable &= ~CONTROLLER_SPDIFOUT_RUNNING; - outl((1 << 23), card->iobase + 0x08); - outb(0, card->iobase + CONTROLLERSPDIFOUT_CR); - while (inb(card->iobase + CONTROLLERSPDIFOUT_CR) != 0) - cpu_relax(); - outb(inb(card->iobase + CONTROLLERSPDIFOUT_SR) | 0x001e, card->iobase + CONTROLLERSPDIFOUT_SR); - outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_SPDIFOUT, card->iobase + ALI_INTERRUPTSR); - } - } -} - -static void stop_spdifout(struct ali_state *state) -{ - struct ali_card *card = state->card; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - __stop_spdifout(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -static inline void __start_spdifout(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && - (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { - if (codec_independent_spdif_locked > 0) { - dmabuf->enable |= CODEC_SPDIFOUT_RUNNING; - outb((1 << 4) | (1 << 2), state->card->iobase + CODECSPDIFOUT_CR); - outl((1 << 3), state->card->iobase + 0x08); //dma control register - } else { - if (controller_independent_spdif_locked > 0) { - dmabuf->enable |= CONTROLLER_SPDIFOUT_RUNNING; - outb((1 << 4) | (1 << 2), state->card->iobase + CONTROLLERSPDIFOUT_CR); - outl((1 << 7), state->card->iobase + 0x08); //dma control register - } - } - } -} - -static void start_spdifout(struct ali_state *state) -{ - struct ali_card *card = state->card; - unsigned long flags; - spin_lock_irqsave(&card->lock, flags); - __start_spdifout(state); - spin_unlock_irqrestore(&card->lock, flags); -} - -#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated separately */ -static int alloc_dmabuf(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - void *rawbuf = NULL; - int order, size; - struct page *page, *pend; - - /* If we don't have any oss frag params, then use our default ones */ - if (dmabuf->ossmaxfrags == 0) - dmabuf->ossmaxfrags = 4; - if (dmabuf->ossfragsize == 0) - dmabuf->ossfragsize = (PAGE_SIZE << DMABUF_DEFAULTORDER) / dmabuf->ossmaxfrags; - size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; - - if (dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) - return 0; - /* alloc enough to satisfy the oss params */ - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { - if ((PAGE_SIZE << order) > size) - continue; - if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, - PAGE_SIZE << order, - &dmabuf->dma_handle))) - break; - } - if (!rawbuf) - return -ENOMEM; - - dmabuf->ready = dmabuf->mapped = 0; - dmabuf->rawbuf = rawbuf; - dmabuf->buforder = order; - - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); - for (page = virt_to_page(rawbuf); page <= pend; page++) - SetPageReserved(page); - return 0; -} - -/* free DMA buffer */ -static void dealloc_dmabuf(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct page *page, *pend; - - if (dmabuf->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); - for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) - ClearPageReserved(page); - pci_free_consistent(state->card->pci_dev, - PAGE_SIZE << dmabuf->buforder, - dmabuf->rawbuf, dmabuf->dma_handle); - } - dmabuf->rawbuf = NULL; - dmabuf->mapped = dmabuf->ready = 0; -} - -static int prog_dmabuf(struct ali_state *state, unsigned rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - struct ali_channel *c = NULL; - struct sg_item *sg; - unsigned long flags; - int ret; - unsigned fragint; - int i; - - spin_lock_irqsave(&state->card->lock, flags); - if (dmabuf->enable & DAC_RUNNING) - __stop_dac(state); - if (dmabuf->enable & ADC_RUNNING) - __stop_adc(state); - if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - __stop_spdifout(state); - if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - __stop_spdifout(state); - - dmabuf->total_bytes = 0; - dmabuf->count = dmabuf->error = 0; - dmabuf->swptr = dmabuf->hwptr = 0; - spin_unlock_irqrestore(&state->card->lock, flags); - - /* allocate DMA buffer, let alloc_dmabuf determine if we are already - * allocated well enough or if we should replace the current buffer - * (assuming one is already allocated, if it isn't, then allocate it). - */ - if ((ret = alloc_dmabuf(state))) - return ret; - - /* FIXME: figure out all this OSS fragment stuff */ - /* I did, it now does what it should according to the OSS API. DL */ - /* We may not have realloced our dmabuf, but the fragment size to - * fragment number ratio may have changed, so go ahead and reprogram - * things - */ - - dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; - dmabuf->numfrag = SG_LEN; - dmabuf->fragsize = dmabuf->dmasize / dmabuf->numfrag; - dmabuf->fragsamples = dmabuf->fragsize >> 1; - dmabuf->userfragsize = dmabuf->ossfragsize; - dmabuf->userfrags = dmabuf->dmasize / dmabuf->ossfragsize; - - memset(dmabuf->rawbuf, 0, dmabuf->dmasize); - - if (dmabuf->ossmaxfrags == 4) { - fragint = 8; - dmabuf->fragshift = 2; - } else if (dmabuf->ossmaxfrags == 8) { - fragint = 4; - dmabuf->fragshift = 3; - } else if (dmabuf->ossmaxfrags == 16) { - fragint = 2; - dmabuf->fragshift = 4; - } else { - fragint = 1; - dmabuf->fragshift = 5; - } - /* - * Now set up the ring - */ - - if (rec == 1) - c = dmabuf->read_channel; - else if (rec == 2) - c = dmabuf->codec_spdifout_channel; - else if (rec == 3) - c = dmabuf->controller_spdifout_channel; - else if (rec == 0) - c = dmabuf->write_channel; - if (c != NULL) { - sg = &c->sg[0]; - /* - * Load up 32 sg entries and take an interrupt at half - * way (we might want more interrupts later..) - */ - for (i = 0; i < dmabuf->numfrag; i++) { - sg->busaddr = - virt_to_bus(dmabuf->rawbuf + - dmabuf->fragsize * i); - // the card will always be doing 16bit stereo - sg->control = dmabuf->fragsamples; - sg->control |= CON_BUFPAD; //I modify - // set us up to get IOC interrupts as often as needed to - // satisfy numfrag requirements, no more - if (((i + 1) % fragint) == 0) { - sg->control |= CON_IOC; - } - sg++; - } - spin_lock_irqsave(&state->card->lock, flags); - outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ - outl(virt_to_bus(&c->sg[0]), state->card->iobase + c->port + OFF_BDBAR); - outb(0, state->card->iobase + c->port + OFF_CIV); - outb(0, state->card->iobase + c->port + OFF_LVI); - spin_unlock_irqrestore(&state->card->lock, flags); - } - /* set the ready flag for the dma buffer */ - dmabuf->ready = 1; - return 0; -} - -static void __ali_update_lvi(struct ali_state *state, int rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - int x, port; - port = state->card->iobase; - if (rec == 1) - port += dmabuf->read_channel->port; - else if (rec == 2) - port += dmabuf->codec_spdifout_channel->port; - else if (rec == 3) - port += dmabuf->controller_spdifout_channel->port; - else if (rec == 0) - port += dmabuf->write_channel->port; - /* if we are currently stopped, then our CIV is actually set to our - * *last* sg segment and we are ready to wrap to the next. However, - * if we set our LVI to the last sg segment, then it won't wrap to - * the next sg segment, it won't even get a start. So, instead, when - * we are stopped, we set both the LVI value and also we increment - * the CIV value to the next sg segment to be played so that when - * we call start_{dac,adc}, things will operate properly - */ - if (!dmabuf->enable && dmabuf->ready) { - if (rec && dmabuf->count < dmabuf->dmasize && (dmabuf->trigger & PCM_ENABLE_INPUT)) { - outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); - __start_adc(state); - while (! (inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) - cpu_relax(); - } else if (!rec && dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { - outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); - __start_dac(state); - while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) - cpu_relax(); - } else if (rec && dmabuf->count && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { - if (codec_independent_spdif_locked > 0) { - // outb((inb(port+OFF_CIV))&31, port+OFF_LVI); - outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); - __start_spdifout(state); - while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) - cpu_relax(); - } else { - if (controller_independent_spdif_locked > 0) { - outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); - __start_spdifout(state); - while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) - cpu_relax(); - } - } - } - } - - /* swptr - 1 is the tail of our transfer */ - x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; - x /= dmabuf->fragsize; - outb(x, port + OFF_LVI); -} - -static void ali_update_lvi(struct ali_state *state, int rec) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - if (!dmabuf->ready) - return; - spin_lock_irqsave(&state->card->lock, flags); - __ali_update_lvi(state, rec); - spin_unlock_irqrestore(&state->card->lock, flags); -} - -/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ -static void ali_update_ptr(struct ali_state *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - unsigned hwptr; - int diff; - - /* error handling and process wake up for DAC */ - if (dmabuf->enable == ADC_RUNNING) { - /* update hardware pointer */ - hwptr = ali_get_dma_addr(state, 1); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; - dmabuf->hwptr = hwptr; - dmabuf->total_bytes += diff; - dmabuf->count += diff; - if (dmabuf->count > dmabuf->dmasize) { - /* buffer underrun or buffer overrun */ - /* this is normal for the end of a read */ - /* only give an error if we went past the */ - /* last valid sg entry */ - if ((inb(state->card->iobase + PI_CIV) & 31) != (inb(state->card->iobase + PI_LVI) & 31)) { - printk(KERN_WARNING "ali_audio: DMA overrun on read\n"); - dmabuf->error++; - } - } - if (dmabuf->count > dmabuf->userfragsize) - wake_up(&dmabuf->wait); - } - /* error handling and process wake up for DAC */ - if (dmabuf->enable == DAC_RUNNING) { - /* update hardware pointer */ - hwptr = ali_get_dma_addr(state, 0); - diff = - (dmabuf->dmasize + hwptr - - dmabuf->hwptr) % dmabuf->dmasize; -#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) - printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); -#endif - dmabuf->hwptr = hwptr; - dmabuf->total_bytes += diff; - dmabuf->count -= diff; - if (dmabuf->count < 0) { - /* buffer underrun or buffer overrun */ - /* this is normal for the end of a write */ - /* only give an error if we went past the */ - /* last valid sg entry */ - if ((inb(state->card->iobase + PO_CIV) & 31) != (inb(state->card->iobase + PO_LVI) & 31)) { - printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); - printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", - inb(state->card->iobase + PO_CIV) & 31, - inb(state->card->iobase + PO_LVI) & 31, - dmabuf->hwptr, - dmabuf->count); - dmabuf->error++; - } - } - if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) - wake_up(&dmabuf->wait); - } - - /* error handling and process wake up for CODEC SPDIF OUT */ - if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { - /* update hardware pointer */ - hwptr = ali_get_dma_addr(state, 2); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; - dmabuf->hwptr = hwptr; - dmabuf->total_bytes += diff; - dmabuf->count -= diff; - if (dmabuf->count < 0) { - /* buffer underrun or buffer overrun */ - /* this is normal for the end of a write */ - /* only give an error if we went past the */ - /* last valid sg entry */ - if ((inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31)) { - printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); - printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", - inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31, - inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31, - dmabuf->hwptr, dmabuf->count); - dmabuf->error++; - } - } - if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) - wake_up(&dmabuf->wait); - } - /* error handling and process wake up for CONTROLLER SPDIF OUT */ - if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { - /* update hardware pointer */ - hwptr = ali_get_dma_addr(state, 3); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; - dmabuf->hwptr = hwptr; - dmabuf->total_bytes += diff; - dmabuf->count -= diff; - if (dmabuf->count < 0) { - /* buffer underrun or buffer overrun */ - /* this is normal for the end of a write */ - /* only give an error if we went past the */ - /* last valid sg entry */ - if ((inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31)) { - printk(KERN_WARNING - "ali_audio: DMA overrun on write\n"); - printk("ali_audio: CIV %d, LVI %d, hwptr %x, " - "count %d\n", - inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31, - inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31, - dmabuf->hwptr, dmabuf->count); - dmabuf->error++; - } - } - if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) - wake_up(&dmabuf->wait); - } -} - -static inline int ali_get_free_write_space(struct - ali_state - *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - int free; - - if (dmabuf->count < 0) { - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - } - free = dmabuf->dmasize - dmabuf->swptr; - if ((dmabuf->count + free) > dmabuf->dmasize){ - free = dmabuf->dmasize - dmabuf->count; - } - return free; -} - -static inline int ali_get_available_read_data(struct - ali_state - *state) -{ - struct dmabuf *dmabuf = &state->dmabuf; - int avail; - ali_update_ptr(state); - // catch overruns during record - if (dmabuf->count > dmabuf->dmasize) { - dmabuf->count = dmabuf->dmasize; - dmabuf->swptr = dmabuf->hwptr; - } - avail = dmabuf->count; - avail -= (dmabuf->hwptr % dmabuf->fragsize); - if (avail < 0) - return (0); - return (avail); -} - -static int drain_dac(struct ali_state *state, int signals_allowed) -{ - - DECLARE_WAITQUEUE(wait, current); - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - unsigned long tmo; - int count; - if (!dmabuf->ready) - return 0; - if (dmabuf->mapped) { - stop_dac(state); - return 0; - } - add_wait_queue(&dmabuf->wait, &wait); - for (;;) { - - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - count = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - if (count <= 0) - break; - /* - * This will make sure that our LVI is correct, that our - * pointer is updated, and that the DAC is running. We - * have to force the setting of dmabuf->trigger to avoid - * any possible deadlocks. - */ - if (!dmabuf->enable) { - dmabuf->trigger = PCM_ENABLE_OUTPUT; - ali_update_lvi(state, 0); - } - if (signal_pending(current) && signals_allowed) { - break; - } - - /* It seems that we have to set the current state to - * TASK_INTERRUPTIBLE every time to make the process - * really go to sleep. This also has to be *after* the - * update_ptr() call because update_ptr is likely to - * do a wake_up() which will unset this before we ever - * try to sleep, resuling in a tight loop in this code - * instead of actually sleeping and waiting for an - * interrupt to wake us up! - */ - set_current_state(TASK_INTERRUPTIBLE); - /* - * set the timeout to significantly longer than it *should* - * take for the DAC to drain the DMA buffer - */ - tmo = (count * HZ) / (dmabuf->rate); - if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { - printk(KERN_ERR "ali_audio: drain_dac, dma timeout?\n"); - count = 0; - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &wait); - if (count > 0 && signal_pending(current) && signals_allowed) - return -ERESTARTSYS; - stop_dac(state); - return 0; -} - - -static int drain_spdifout(struct ali_state *state, int signals_allowed) -{ - - DECLARE_WAITQUEUE(wait, current); - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - unsigned long tmo; - int count; - if (!dmabuf->ready) - return 0; - if (dmabuf->mapped) { - stop_spdifout(state); - return 0; - } - add_wait_queue(&dmabuf->wait, &wait); - for (;;) { - - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - count = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - if (count <= 0) - break; - /* - * This will make sure that our LVI is correct, that our - * pointer is updated, and that the DAC is running. We - * have to force the setting of dmabuf->trigger to avoid - * any possible deadlocks. - */ - if (!dmabuf->enable) { - if (codec_independent_spdif_locked > 0) { - dmabuf->trigger = SPDIF_ENABLE_OUTPUT; - ali_update_lvi(state, 2); - } else { - if (controller_independent_spdif_locked > 0) { - dmabuf->trigger = SPDIF_ENABLE_OUTPUT; - ali_update_lvi(state, 3); - } - } - } - if (signal_pending(current) && signals_allowed) { - break; - } - - /* It seems that we have to set the current state to - * TASK_INTERRUPTIBLE every time to make the process - * really go to sleep. This also has to be *after* the - * update_ptr() call because update_ptr is likely to - * do a wake_up() which will unset this before we ever - * try to sleep, resuling in a tight loop in this code - * instead of actually sleeping and waiting for an - * interrupt to wake us up! - */ - set_current_state(TASK_INTERRUPTIBLE); - /* - * set the timeout to significantly longer than it *should* - * take for the DAC to drain the DMA buffer - */ - tmo = (count * HZ) / (dmabuf->rate); - if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { - printk(KERN_ERR "ali_audio: drain_spdifout, dma timeout?\n"); - count = 0; - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &wait); - if (count > 0 && signal_pending(current) && signals_allowed) - return -ERESTARTSYS; - stop_spdifout(state); - return 0; -} - -static void ali_channel_interrupt(struct ali_card *card) -{ - int i, count; - - for (i = 0; i < NR_HW_CH; i++) { - struct ali_state *state = card->states[i]; - struct ali_channel *c = NULL; - struct dmabuf *dmabuf; - unsigned long port = card->iobase; - u16 status; - if (!state) - continue; - if (!state->dmabuf.ready) - continue; - dmabuf = &state->dmabuf; - if (codec_independent_spdif_locked > 0) { - if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { - c = dmabuf->codec_spdifout_channel; - } - } else { - if (controller_independent_spdif_locked > 0) { - if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - c = dmabuf->controller_spdifout_channel; - } else { - if (dmabuf->enable & DAC_RUNNING) { - c = dmabuf->write_channel; - } else if (dmabuf->enable & ADC_RUNNING) { - c = dmabuf->read_channel; - } else - continue; - } - } - port += c->port; - - status = inw(port + OFF_SR); - - if (status & DMA_INT_COMPLETE) { - /* only wake_up() waiters if this interrupt signals - * us being beyond a userfragsize of data open or - * available, and ali_update_ptr() does that for - * us - */ - ali_update_ptr(state); - } - - if (status & DMA_INT_LVI) { - ali_update_ptr(state); - wake_up(&dmabuf->wait); - - if (dmabuf->enable & DAC_RUNNING) - count = dmabuf->count; - else if (dmabuf->enable & ADC_RUNNING) - count = dmabuf->dmasize - dmabuf->count; - else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - count = dmabuf->count; - else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - count = dmabuf->count; - else count = 0; - - if (count > 0) { - if (dmabuf->enable & DAC_RUNNING) - outl((1 << 1), state->card->iobase + ALI_DMACR); - else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - outl((1 << 3), state->card->iobase + ALI_DMACR); - else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - outl((1 << 7), state->card->iobase + ALI_DMACR); - } else { - if (dmabuf->enable & DAC_RUNNING) - __stop_dac(state); - if (dmabuf->enable & ADC_RUNNING) - __stop_adc(state); - if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - __stop_spdifout(state); - if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - __stop_spdifout(state); - dmabuf->enable = 0; - wake_up(&dmabuf->wait); - } - - } - if (!(status & DMA_INT_DCH)) { - ali_update_ptr(state); - wake_up(&dmabuf->wait); - if (dmabuf->enable & DAC_RUNNING) - count = dmabuf->count; - else if (dmabuf->enable & ADC_RUNNING) - count = dmabuf->dmasize - dmabuf->count; - else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - count = dmabuf->count; - else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - count = dmabuf->count; - else - count = 0; - - if (count > 0) { - if (dmabuf->enable & DAC_RUNNING) - outl((1 << 1), state->card->iobase + ALI_DMACR); - else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - outl((1 << 3), state->card->iobase + ALI_DMACR); - else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - outl((1 << 7), state->card->iobase + ALI_DMACR); - } else { - if (dmabuf->enable & DAC_RUNNING) - __stop_dac(state); - if (dmabuf->enable & ADC_RUNNING) - __stop_adc(state); - if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) - __stop_spdifout(state); - if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) - __stop_spdifout(state); - dmabuf->enable = 0; - wake_up(&dmabuf->wait); - } - } - outw(status & DMA_INT_MASK, port + OFF_SR); - } -} - -static irqreturn_t ali_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct ali_card *card = (struct ali_card *) dev_id; - u32 status; - u16 status2; - - spin_lock(&card->lock); - status = inl(card->iobase + ALI_INTERRUPTSR); - if (!(status & INT_MASK)) { - spin_unlock(&card->lock); - return IRQ_NONE; /* not for us */ - } - - if (codec_independent_spdif_locked > 0) { - if (globel == 0) { - globel += 1; - status2 = inw(card->iobase + 0x76); - outw(status2 | 0x000c, card->iobase + 0x76); - } else { - if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) - ali_channel_interrupt(card); - } - } else { - if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) - ali_channel_interrupt(card); - } - - /* clear 'em */ - outl(status & INT_MASK, card->iobase + ALI_INTERRUPTSR); - spin_unlock(&card->lock); - return IRQ_HANDLED; -} - -/* in this loop, dmabuf.count signifies the amount of data that is - waiting to be copied to the user's buffer. It is filled by the dma - machine and drained by this loop. */ - -static ssize_t ali_read(struct file *file, char __user *buffer, - size_t count, loff_t * ppos) -{ - struct ali_state *state = (struct ali_state *) file->private_data; - struct ali_card *card = state ? state->card : NULL; - struct dmabuf *dmabuf = &state->dmabuf; - ssize_t ret; - unsigned long flags; - unsigned int swptr; - int cnt; - DECLARE_WAITQUEUE(waita, current); -#ifdef DEBUG2 - printk("ali_audio: ali_read called, count = %d\n", count); -#endif - if (dmabuf->mapped) - return -ENXIO; - if (dmabuf->enable & DAC_RUNNING) - return -ENODEV; - if (!dmabuf->read_channel) { - dmabuf->ready = 0; - dmabuf->read_channel = card->alloc_rec_pcm_channel(card); - if (!dmabuf->read_channel) { - return -EBUSY; - } - } - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - add_wait_queue(&dmabuf->wait, &waita); - while (count > 0) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&card->lock, flags); - if (PM_SUSPENDED(card)) { - spin_unlock_irqrestore(&card->lock, flags); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -EAGAIN; - break; - } - continue; - } - swptr = dmabuf->swptr; - cnt = ali_get_available_read_data(state); - // this is to make the copy_to_user simpler below - if (cnt > (dmabuf->dmasize - swptr)) - cnt = dmabuf->dmasize - swptr; - spin_unlock_irqrestore(&card->lock, flags); - if (cnt > count) - cnt = count; - /* Lop off the last two bits to force the code to always - * write in full samples. This keeps software that sets - * O_NONBLOCK but doesn't check the return value of the - * write call from getting things out of state where they - * think a full 4 byte sample was written when really only - * a portion was, resulting in odd sound and stereo - * hysteresis. - */ - cnt &= ~0x3; - if (cnt <= 0) { - unsigned long tmo; - /* - * Don't let us deadlock. The ADC won't start if - * dmabuf->trigger isn't set. A call to SETTRIGGER - * could have turned it off after we set it to on - * previously. - */ - dmabuf->trigger = PCM_ENABLE_INPUT; - /* - * This does three things. Updates LVI to be correct, - * makes sure the ADC is running, and updates the - * hwptr. - */ - ali_update_lvi(state, 1); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto done; - } - /* Set the timeout to how long it would take to fill - * two of our buffers. If we haven't been woke up - * by then, then we know something is wrong. - */ - tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); - - /* There are two situations when sleep_on_timeout returns, one is when - the interrupt is serviced correctly and the process is waked up by - ISR ON TIME. Another is when timeout is expired, which means that - either interrupt is NOT serviced correctly (pending interrupt) or it - is TOO LATE for the process to be scheduled to run (scheduler latency) - which results in a (potential) buffer overrun. And worse, there is - NOTHING we can do to prevent it. */ - if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { - printk(KERN_ERR - "ali_audio: recording schedule timeout, " - "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - dmabuf->dmasize, dmabuf->fragsize, - dmabuf->count, dmabuf->hwptr, - dmabuf->swptr); - /* a buffer overrun, we delay the recovery until next time the - while loop begin and we REALLY have space to record */ - } - if (signal_pending(current)) { - ret = ret ? ret : -ERESTARTSYS; - goto done; - } - continue; - } - - if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - goto done; - } - - swptr = (swptr + cnt) % dmabuf->dmasize; - spin_lock_irqsave(&card->lock, flags); - if (PM_SUSPENDED(card)) { - spin_unlock_irqrestore(&card->lock, flags); - continue; - } - dmabuf->swptr = swptr; - dmabuf->count -= cnt; - spin_unlock_irqrestore(&card->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - } -done: - ali_update_lvi(state, 1); - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &waita); - return ret; -} - -/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to - the soundcard. it is drained by the dma machine and filled by this loop. */ -static ssize_t ali_write(struct file *file, - const char __user *buffer, size_t count, loff_t * ppos) -{ - struct ali_state *state = (struct ali_state *) file->private_data; - struct ali_card *card = state ? state->card : NULL; - struct dmabuf *dmabuf = &state->dmabuf; - ssize_t ret; - unsigned long flags; - unsigned int swptr = 0; - int cnt, x; - DECLARE_WAITQUEUE(waita, current); -#ifdef DEBUG2 - printk("ali_audio: ali_write called, count = %d\n", count); -#endif - if (dmabuf->mapped) - return -ENXIO; - if (dmabuf->enable & ADC_RUNNING) - return -ENODEV; - if (codec_independent_spdif_locked > 0) { - if (!dmabuf->codec_spdifout_channel) { - dmabuf->ready = 0; - dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card); - if (!dmabuf->codec_spdifout_channel) - return -EBUSY; - } - } else { - if (controller_independent_spdif_locked > 0) { - if (!dmabuf->controller_spdifout_channel) { - dmabuf->ready = 0; - dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card); - if (!dmabuf->controller_spdifout_channel) - return -EBUSY; - } - } else { - if (!dmabuf->write_channel) { - dmabuf->ready = 0; - dmabuf->write_channel = - card->alloc_pcm_channel(card); - if (!dmabuf->write_channel) - return -EBUSY; - } - } - } - - if (codec_independent_spdif_locked > 0) { - if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) - return ret; - } else { - if (controller_independent_spdif_locked > 0) { - if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) - return ret; - } else { - - if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - return ret; - } - } - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - add_wait_queue(&dmabuf->wait, &waita); - while (count > 0) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&state->card->lock, flags); - if (PM_SUSPENDED(card)) { - spin_unlock_irqrestore(&card->lock, flags); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -EAGAIN; - break; - } - continue; - } - - swptr = dmabuf->swptr; - cnt = ali_get_free_write_space(state); - /* Bound the maximum size to how much we can copy to the - * dma buffer before we hit the end. If we have more to - * copy then it will get done in a second pass of this - * loop starting from the beginning of the buffer. - */ - if (cnt > (dmabuf->dmasize - swptr)) - cnt = dmabuf->dmasize - swptr; - spin_unlock_irqrestore(&state->card->lock, flags); -#ifdef DEBUG2 - printk(KERN_INFO - "ali_audio: ali_write: %d bytes available space\n", - cnt); -#endif - if (cnt > count) - cnt = count; - /* Lop off the last two bits to force the code to always - * write in full samples. This keeps software that sets - * O_NONBLOCK but doesn't check the return value of the - * write call from getting things out of state where they - * think a full 4 byte sample was written when really only - * a portion was, resulting in odd sound and stereo - * hysteresis. - */ - cnt &= ~0x3; - if (cnt <= 0) { - unsigned long tmo; - // There is data waiting to be played - /* - * Force the trigger setting since we would - * deadlock with it set any other way - */ - if (codec_independent_spdif_locked > 0) { - dmabuf->trigger = SPDIF_ENABLE_OUTPUT; - ali_update_lvi(state, 2); - } else { - if (controller_independent_spdif_locked > 0) { - dmabuf->trigger = SPDIF_ENABLE_OUTPUT; - ali_update_lvi(state, 3); - } else { - - dmabuf->trigger = PCM_ENABLE_OUTPUT; - ali_update_lvi(state, 0); - } - } - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto ret; - } - /* Not strictly correct but works */ - tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); - /* There are two situations when sleep_on_timeout returns, one is when - the interrupt is serviced correctly and the process is waked up by - ISR ON TIME. Another is when timeout is expired, which means that - either interrupt is NOT serviced correctly (pending interrupt) or it - is TOO LATE for the process to be scheduled to run (scheduler latency) - which results in a (potential) buffer underrun. And worse, there is - NOTHING we can do to prevent it. */ - - /* FIXME - do timeout handling here !! */ - schedule_timeout(tmo >= 2 ? tmo : 2); - - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto ret; - } - continue; - } - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - goto ret; - } - - swptr = (swptr + cnt) % dmabuf->dmasize; - spin_lock_irqsave(&state->card->lock, flags); - if (PM_SUSPENDED(card)) { - spin_unlock_irqrestore(&card->lock, flags); - continue; - } - - dmabuf->swptr = swptr; - dmabuf->count += cnt; - count -= cnt; - buffer += cnt; - ret += cnt; - spin_unlock_irqrestore(&state->card->lock, flags); - } - if (swptr % dmabuf->fragsize) { - x = dmabuf->fragsize - (swptr % dmabuf->fragsize); - memset(dmabuf->rawbuf + swptr, '\0', x); - } -ret: - if (codec_independent_spdif_locked > 0) { - ali_update_lvi(state, 2); - } else { - if (controller_independent_spdif_locked > 0) { - ali_update_lvi(state, 3); - } else { - ali_update_lvi(state, 0); - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &waita); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int ali_poll(struct file *file, struct poll_table_struct - *wait) -{ - struct ali_state *state = (struct ali_state *) file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - unsigned int mask = 0; - if (!dmabuf->ready) - return 0; - poll_wait(file, &dmabuf->wait, wait); - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) { - if (dmabuf->count >= (signed) dmabuf->fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE && (dmabuf->enable & (DAC_RUNNING|CODEC_SPDIFOUT_RUNNING|CONTROLLER_SPDIFOUT_RUNNING))) { - if ((signed) dmabuf->dmasize >= dmabuf->count + (signed) dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&state->card->lock, flags); - return mask; -} - -static int ali_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ali_state *state = (struct ali_state *) file->private_data; - struct dmabuf *dmabuf = &state->dmabuf; - int ret = -EINVAL; - unsigned long size; - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL) { - ret = -EBUSY; - goto out; - } - } - if (vma->vm_flags & VM_READ) { - if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL) { - ret = -EBUSY; - goto out; - } - } - if ((ret = prog_dmabuf(state, 0)) != 0) - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << dmabuf->buforder)) - goto out; - ret = -EAGAIN; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - dmabuf->mapped = 1; - dmabuf->trigger = 0; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct ali_state *state = (struct ali_state *) file->private_data; - struct ali_channel *c = NULL; - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - unsigned int i_scr; - int val = 0, ret; - struct ac97_codec *codec = state->card->ac97_codec[0]; - void __user *argp = (void __user *)arg; - int __user *p = argp; - -#ifdef DEBUG - printk("ali_audio: ali_ioctl, arg=0x%x, cmd=", - arg ? *p : 0); -#endif - switch (cmd) { - case OSS_GETVERSION: -#ifdef DEBUG - printk("OSS_GETVERSION\n"); -#endif - return put_user(SOUND_VERSION, p); - case SNDCTL_DSP_RESET: -#ifdef DEBUG - printk("SNDCTL_DSP_RESET\n"); -#endif - spin_lock_irqsave(&state->card->lock, flags); - if (dmabuf->enable == DAC_RUNNING) { - c = dmabuf->write_channel; - __stop_dac(state); - } - if (dmabuf->enable == ADC_RUNNING) { - c = dmabuf->read_channel; - __stop_adc(state); - } - if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { - c = dmabuf->codec_spdifout_channel; - __stop_spdifout(state); - } - if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { - c = dmabuf->controller_spdifout_channel; - __stop_spdifout(state); - } - if (c != NULL) { - outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ - outl(virt_to_bus(&c->sg[0]), - state->card->iobase + c->port + OFF_BDBAR); - outb(0, state->card->iobase + c->port + OFF_CIV); - outb(0, state->card->iobase + c->port + OFF_LVI); - } - - spin_unlock_irqrestore(&state->card->lock, flags); - synchronize_irq(state->card->pci_dev->irq); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr = 0; - dmabuf->count = dmabuf->total_bytes = 0; - return 0; - case SNDCTL_DSP_SYNC: -#ifdef DEBUG - printk("SNDCTL_DSP_SYNC\n"); -#endif - if (codec_independent_spdif_locked > 0) { - if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING - || file->f_flags & O_NONBLOCK) - return 0; - if ((val = drain_spdifout(state, 1))) - return val; - } else { - if (controller_independent_spdif_locked > 0) { - if (dmabuf->enable != - CONTROLLER_SPDIFOUT_RUNNING - || file->f_flags & O_NONBLOCK) - return 0; - if ((val = drain_spdifout(state, 1))) - return val; - } else { - if (dmabuf->enable != DAC_RUNNING - || file->f_flags & O_NONBLOCK) - return 0; - if ((val = drain_dac(state, 1))) - return val; - } - } - dmabuf->total_bytes = 0; - return 0; - case SNDCTL_DSP_SPEED: /* set smaple rate */ -#ifdef DEBUG - printk("SNDCTL_DSP_SPEED\n"); -#endif - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_WRITE) { - if ((state->card->ac97_status & SPDIF_ON)) { /* S/PDIF Enabled */ - /* RELTEK ALC650 only support 48000, need to check that */ - if (ali_valid_spdif_rate(codec, val)) { - if (codec_independent_spdif_locked > 0) { - ali_set_spdif_output(state, -1, 0); - stop_spdifout(state); - dmabuf->ready = 0; - /* I add test codec independent spdif out */ - spin_lock_irqsave(&state->card->lock, flags); - ali_set_codecspdifout_rate(state, val); // I modified - spin_unlock_irqrestore(&state->card->lock, flags); - /* Set S/PDIF transmitter rate. */ - i_scr = inl(state->card->iobase + ALI_SCR); - if ((i_scr & 0x00300000) == 0x00100000) { - ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); - } else { - if ((i_scr&0x00300000) == 0x00200000) - { - ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); - } else { - if ((i_scr & 0x00300000) == 0x00300000) { - ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); - } else { - ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); - } - } - } - - if (!(state->card->ac97_status & SPDIF_ON)) { - val = dmabuf->rate; - } - } else { - if (controller_independent_spdif_locked > 0) - { - stop_spdifout(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - ali_set_spdifout_rate(state, controller_independent_spdif_locked); - spin_unlock_irqrestore(&state->card->lock, flags); - } else { - /* Set DAC rate */ - ali_set_spdif_output(state, -1, 0); - stop_dac(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - ali_set_dac_rate(state, val); - spin_unlock_irqrestore(&state->card->lock, flags); - /* Set S/PDIF transmitter rate. */ - ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val); - if (!(state->card->ac97_status & SPDIF_ON)) - { - val = dmabuf->rate; - } - } - } - } else { /* Not a valid rate for S/PDIF, ignore it */ - val = dmabuf->rate; - } - } else { - stop_dac(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - ali_set_dac_rate(state, val); - spin_unlock_irqrestore(&state->card->lock, flags); - } - } - if (file->f_mode & FMODE_READ) { - stop_adc(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - ali_set_adc_rate(state, val); - spin_unlock_irqrestore(&state->card->lock, flags); - } - } - return put_user(dmabuf->rate, p); - case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ -#ifdef DEBUG - printk("SNDCTL_DSP_STEREO\n"); -#endif - if (dmabuf->enable & DAC_RUNNING) { - stop_dac(state); - } - if (dmabuf->enable & ADC_RUNNING) { - stop_adc(state); - } - if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { - stop_spdifout(state); - } - if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { - stop_spdifout(state); - } - return put_user(1, p); - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if (codec_independent_spdif_locked > 0) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 2))) - return val; - } else { - if (controller_independent_spdif_locked > 0) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 3))) - return val; - } else { - if (!dmabuf->ready && (val = prog_dmabuf(state, 0))) - return val; - } - } - } - - if (file->f_mode & FMODE_READ) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 1))) - return val; - } -#ifdef DEBUG - printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize); -#endif - return put_user(dmabuf->userfragsize, p); - case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */ -#ifdef DEBUG - printk("SNDCTL_DSP_GETFMTS\n"); -#endif - return put_user(AFMT_S16_LE, p); - case SNDCTL_DSP_SETFMT: /* Select sample format */ -#ifdef DEBUG - printk("SNDCTL_DSP_SETFMT\n"); -#endif - return put_user(AFMT_S16_LE, p); - case SNDCTL_DSP_CHANNELS: // add support 4,6 channel -#ifdef DEBUG - printk("SNDCTL_DSP_CHANNELS\n"); -#endif - if (get_user(val, p)) - return -EFAULT; - if (val > 0) { - if (dmabuf->enable & DAC_RUNNING) { - stop_dac(state); - } - if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { - stop_spdifout(state); - } - if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { - stop_spdifout(state); - } - if (dmabuf->enable & ADC_RUNNING) { - stop_adc(state); - } - } else { - return put_user(state->card->channels, p); - } - - i_scr = inl(state->card->iobase + ALI_SCR); - /* Current # of channels enabled */ - if (i_scr & 0x00000100) - ret = 4; - else if (i_scr & 0x00000200) - ret = 6; - else - ret = 2; - switch (val) { - case 2: /* 2 channels is always supported */ - if (codec_independent_spdif_locked > 0) { - outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR)); - } else - outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR)); - /* Do we need to change mixer settings???? */ - break; - case 4: /* Supported on some chipsets, better check first */ - if (codec_independent_spdif_locked > 0) { - outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR)); - } else - outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR)); - break; - case 6: /* Supported on some chipsets, better check first */ - if (codec_independent_spdif_locked > 0) { - outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR)); - } else - outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR)); - break; - default: /* nothing else is ever supported by the chipset */ - val = ret; - break; - } - return put_user(val, p); - case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ - /* we update the swptr to the end of the last sg segment then return */ -#ifdef DEBUG - printk("SNDCTL_DSP_POST\n"); -#endif - if (codec_independent_spdif_locked > 0) { - if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING)) - return 0; - } else { - if (controller_independent_spdif_locked > 0) { - if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING)) - return 0; - } else { - if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING)) - return 0; - } - } - if ((dmabuf->swptr % dmabuf->fragsize) != 0) { - val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize); - dmabuf->swptr += val; - dmabuf->count += val; - } - return 0; - case SNDCTL_DSP_SUBDIVIDE: - if (dmabuf->subdivision) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; -#ifdef DEBUG - printk("SNDCTL_DSP_SUBDIVIDE %d\n", val); -#endif - dmabuf->subdivision = val; - dmabuf->ready = 0; - return 0; - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - dmabuf->ossfragsize = 1 << (val & 0xffff); - dmabuf->ossmaxfrags = (val >> 16) & 0xffff; - if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) - return -EINVAL; - /* - * Bound the frag size into our allowed range of 256 - 4096 - */ - if (dmabuf->ossfragsize < 256) - dmabuf->ossfragsize = 256; - else if (dmabuf->ossfragsize > 4096) - dmabuf->ossfragsize = 4096; - /* - * The numfrags could be something reasonable, or it could - * be 0xffff meaning "Give me as much as possible". So, - * we check the numfrags * fragsize doesn't exceed our - * 64k buffer limit, nor is it less than our 8k minimum. - * If it fails either one of these checks, then adjust the - * number of fragments, not the size of them. It's OK if - * our number of fragments doesn't equal 32 or anything - * like our hardware based number now since we are using - * a different frag count for the hardware. Before we get - * into this though, bound the maxfrags to avoid overflow - * issues. A reasonable bound would be 64k / 256 since our - * maximum buffer size is 64k and our minimum frag size is - * 256. On the other end, our minimum buffer size is 8k and - * our maximum frag size is 4k, so the lower bound should - * be 2. - */ - if (dmabuf->ossmaxfrags > 256) - dmabuf->ossmaxfrags = 256; - else if (dmabuf->ossmaxfrags < 2) - dmabuf->ossmaxfrags = 2; - val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; - while (val < 8192) { - val <<= 1; - dmabuf->ossmaxfrags <<= 1; - } - while (val > 65536) { - val >>= 1; - dmabuf->ossmaxfrags >>= 1; - } - dmabuf->ready = 0; -#ifdef DEBUG - printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val, - dmabuf->ossfragsize, dmabuf->ossmaxfrags); -#endif - return 0; - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (codec_independent_spdif_locked > 0) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) - return val; - } else { - if (controller_independent_spdif_locked > 0) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) - return val; - } else { - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - } - } - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - abinfo.fragsize = dmabuf->userfragsize; - abinfo.fragstotal = dmabuf->userfrags; - if (dmabuf->mapped) - abinfo.bytes = dmabuf->dmasize; - else - abinfo.bytes = ali_get_free_write_space(state); - abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; - spin_unlock_irqrestore(&state->card->lock, flags); -#if defined(DEBUG) || defined(DEBUG_MMAP) - printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", - abinfo.bytes, abinfo.fragsize, abinfo.fragments, - abinfo.fragstotal); -#endif - return copy_to_user(argp, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (codec_independent_spdif_locked > 0) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) - return val; - } else { - if (controller_independent_spdif_locked > 0) { - if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) - return val; - } else { - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - } - } - spin_lock_irqsave(&state->card->lock, flags); - val = ali_get_free_write_space(state); - cinfo.bytes = dmabuf->total_bytes; - cinfo.ptr = dmabuf->hwptr; - cinfo.blocks = val / dmabuf->userfragsize; - if (codec_independent_spdif_locked > 0) { - if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { - dmabuf->count += val; - dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; - __ali_update_lvi(state, 2); - } - } else { - if (controller_independent_spdif_locked > 0) { - if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { - dmabuf->count += val; - dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; - __ali_update_lvi(state, 3); - } - } else { - if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { - dmabuf->count += val; - dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; - __ali_update_lvi(state, 0); - } - } - } - spin_unlock_irqrestore(&state->card->lock, flags); -#if defined(DEBUG) || defined(DEBUG_MMAP) - printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes, - cinfo.blocks, cinfo.ptr, dmabuf->count); -#endif - return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT : 0; - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - return val; - spin_lock_irqsave(&state->card->lock, flags); - abinfo.bytes = ali_get_available_read_data(state); - abinfo.fragsize = dmabuf->userfragsize; - abinfo.fragstotal = dmabuf->userfrags; - abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; - spin_unlock_irqrestore(&state->card->lock, flags); -#if defined(DEBUG) || defined(DEBUG_MMAP) - printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", - abinfo.bytes, abinfo.fragsize, abinfo.fragments, - abinfo.fragstotal); -#endif - return copy_to_user(argp, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - spin_lock_irqsave(&state->card->lock, flags); - val = ali_get_available_read_data(state); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = val / dmabuf->userfragsize; - cinfo.ptr = dmabuf->hwptr; - if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) { - dmabuf->count -= val; - dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; - __ali_update_lvi(state, 1); - } - spin_unlock_irqrestore(&state->card->lock, flags); -#if defined(DEBUG) || defined(DEBUG_MMAP) - printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes, - cinfo.blocks, cinfo.ptr, dmabuf->count); -#endif - return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT: 0; - case SNDCTL_DSP_NONBLOCK: -#ifdef DEBUG - printk("SNDCTL_DSP_NONBLOCK\n"); -#endif - file->f_flags |= O_NONBLOCK; - return 0; - case SNDCTL_DSP_GETCAPS: -#ifdef DEBUG - printk("SNDCTL_DSP_GETCAPS\n"); -#endif - return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | - DSP_CAP_MMAP | DSP_CAP_BIND, p); - case SNDCTL_DSP_GETTRIGGER: - val = 0; -#ifdef DEBUG - printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger); -#endif - return put_user(dmabuf->trigger, p); - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; -#if defined(DEBUG) || defined(DEBUG_MMAP) - printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val); -#endif - if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) { - stop_adc(state); - } - if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) { - stop_dac(state); - } - if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { - stop_spdifout(state); - } - if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { - stop_spdifout(state); - } - dmabuf->trigger = val; - if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) { - if (!dmabuf->write_channel) { - dmabuf->ready = 0; - dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); - if (!dmabuf->write_channel) - return -EBUSY; - } - if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - return ret; - if (dmabuf->mapped) { - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = ali_get_free_write_space(state); - dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; - __ali_update_lvi(state, 0); - spin_unlock_irqrestore(&state->card->lock, - flags); - } else - start_dac(state); - } - if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) { - if (!dmabuf->codec_spdifout_channel) { - dmabuf->ready = 0; - dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card); - if (!dmabuf->codec_spdifout_channel) - return -EBUSY; - } - if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) - return ret; - if (dmabuf->mapped) { - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = ali_get_free_write_space(state); - dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; - __ali_update_lvi(state, 2); - spin_unlock_irqrestore(&state->card->lock, - flags); - } else - start_spdifout(state); - } - if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) { - if (!dmabuf->controller_spdifout_channel) { - dmabuf->ready = 0; - dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card); - if (!dmabuf->controller_spdifout_channel) - return -EBUSY; - } - if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) - return ret; - if (dmabuf->mapped) { - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - dmabuf->count = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = ali_get_free_write_space(state); - dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; - __ali_update_lvi(state, 3); - spin_unlock_irqrestore(&state->card->lock, flags); - } else - start_spdifout(state); - } - if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) { - if (!dmabuf->read_channel) { - dmabuf->ready = 0; - dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); - if (!dmabuf->read_channel) - return -EBUSY; - } - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - return ret; - if (dmabuf->mapped) { - spin_lock_irqsave(&state->card->lock, - flags); - ali_update_ptr(state); - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = 0; - spin_unlock_irqrestore(&state->card->lock, flags); - } - ali_update_lvi(state, 1); - start_adc(state); - } - return 0; - case SNDCTL_DSP_SETDUPLEX: -#ifdef DEBUG - printk("SNDCTL_DSP_SETDUPLEX\n"); -#endif - return -EINVAL; - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&state->card->lock, flags); - ali_update_ptr(state); - val = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); -#ifdef DEBUG - printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count); -#endif - return put_user(val, p); - case SOUND_PCM_READ_RATE: -#ifdef DEBUG - printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate); -#endif - return put_user(dmabuf->rate, p); - case SOUND_PCM_READ_CHANNELS: -#ifdef DEBUG - printk("SOUND_PCM_READ_CHANNELS\n"); -#endif - return put_user(2, p); - case SOUND_PCM_READ_BITS: -#ifdef DEBUG - printk("SOUND_PCM_READ_BITS\n"); -#endif - return put_user(AFMT_S16_LE, p); - case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */ -#ifdef DEBUG - printk("SNDCTL_DSP_SETSPDIF\n"); -#endif - if (get_user(val, p)) - return -EFAULT; - /* Check to make sure the codec supports S/PDIF transmitter */ - if ((state->card->ac97_features & 4)) { - /* mask out the transmitter speed bits so the user can't set them */ - val &= ~0x3000; - /* Add the current transmitter speed bits to the passed value */ - ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL); - val |= (ret & 0x3000); - ali_ac97_set(codec, AC97_SPDIF_CONTROL, val); - if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) { - printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); - return -EFAULT; - } - } -#ifdef DEBUG - else - printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); -#endif - return put_user(val, p); - case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */ -#ifdef DEBUG - printk("SNDCTL_DSP_GETSPDIF\n"); -#endif - if (get_user(val, p)) - return -EFAULT; - /* Check to make sure the codec supports S/PDIF transmitter */ - if (!(state->card->ac97_features & 4)) { -#ifdef DEBUG - printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); -#endif - val = 0; - } else { - val = ali_ac97_get(codec, AC97_SPDIF_CONTROL); - } - - return put_user(val, p); -//end add support spdif out -//add support 4,6 channel - case SNDCTL_DSP_GETCHANNELMASK: -#ifdef DEBUG - printk("SNDCTL_DSP_GETCHANNELMASK\n"); -#endif - if (get_user(val, p)) - return -EFAULT; - /* Based on AC'97 DAC support, not ICH hardware */ - val = DSP_BIND_FRONT; - if (state->card->ac97_features & 0x0004) - val |= DSP_BIND_SPDIF; - if (state->card->ac97_features & 0x0080) - val |= DSP_BIND_SURR; - if (state->card->ac97_features & 0x0140) - val |= DSP_BIND_CENTER_LFE; - return put_user(val, p); - case SNDCTL_DSP_BIND_CHANNEL: -#ifdef DEBUG - printk("SNDCTL_DSP_BIND_CHANNEL\n"); -#endif - if (get_user(val, p)) - return -EFAULT; - if (val == DSP_BIND_QUERY) { - val = DSP_BIND_FRONT; /* Always report this as being enabled */ - if (state->card->ac97_status & SPDIF_ON) - val |= DSP_BIND_SPDIF; - else { - if (state->card->ac97_status & SURR_ON) - val |= DSP_BIND_SURR; - if (state->card-> - ac97_status & CENTER_LFE_ON) - val |= DSP_BIND_CENTER_LFE; - } - } else { /* Not a query, set it */ - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (dmabuf->enable == DAC_RUNNING) { - stop_dac(state); - } - if (val & DSP_BIND_SPDIF) { /* Turn on SPDIF */ - /* Ok, this should probably define what slots - * to use. For now, we'll only set it to the - * defaults: - * - * non multichannel codec maps to slots 3&4 - * 2 channel codec maps to slots 7&8 - * 4 channel codec maps to slots 6&9 - * 6 channel codec maps to slots 10&11 - * - * there should be some way for the app to - * select the slot assignment. - */ - i_scr = inl(state->card->iobase + ALI_SCR); - if (codec_independent_spdif_locked > 0) { - - if ((i_scr & 0x00300000) == 0x00100000) { - ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); - } else { - if ((i_scr & 0x00300000) == 0x00200000) { - ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); - } else { - if ((i_scr & 0x00300000) == 0x00300000) { - ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); - } - } - } - } else { /* codec spdif out (pcm out share ) */ - ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate); //I do not modify - } - - if (!(state->card->ac97_status & SPDIF_ON)) - val &= ~DSP_BIND_SPDIF; - } else { - int mask; - int channels; - /* Turn off S/PDIF if it was on */ - if (state->card->ac97_status & SPDIF_ON) - ali_set_spdif_output(state, -1, 0); - mask = - val & (DSP_BIND_FRONT | DSP_BIND_SURR | - DSP_BIND_CENTER_LFE); - switch (mask) { - case DSP_BIND_FRONT: - channels = 2; - break; - case DSP_BIND_FRONT | DSP_BIND_SURR: - channels = 4; - break; - case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE: - channels = 6; - break; - default: - val = DSP_BIND_FRONT; - channels = 2; - break; - } - ali_set_dac_channels(state, channels); - /* check that they really got turned on */ - if (!state->card->ac97_status & SURR_ON) - val &= ~DSP_BIND_SURR; - if (!state->card-> - ac97_status & CENTER_LFE_ON) - val &= ~DSP_BIND_CENTER_LFE; - } - } - return put_user(val, p); - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_WRITE_FILTER: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } - return -EINVAL; -} - -static int ali_open(struct inode *inode, struct file *file) -{ - int i = 0; - struct ali_card *card = devs; - struct ali_state *state = NULL; - struct dmabuf *dmabuf = NULL; - unsigned int i_scr; - - /* find an available virtual channel (instance of /dev/dsp) */ - - while (card != NULL) { - - /* - * If we are initializing and then fail, card could go - * away unuexpectedly while we are in the for() loop. - * So, check for card on each iteration before we check - * for card->initializing to avoid a possible oops. - * This usually only matters for times when the driver is - * autoloaded by kmod. - */ - for (i = 0; i < 50 && card && card->initializing; i++) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 20); - } - - for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) { - if (card->states[i] == NULL) { - state = card->states[i] = (struct ali_state *) kmalloc(sizeof(struct ali_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; - memset(state, 0, sizeof(struct ali_state)); - dmabuf = &state->dmabuf; - goto found_virt; - } - } - card = card->next; - } - - /* no more virtual channel avaiable */ - if (!state) - return -ENODEV; -found_virt: - /* initialize the virtual channel */ - - state->virt = i; - state->card = card; - state->magic = ALI5455_STATE_MAGIC; - init_waitqueue_head(&dmabuf->wait); - mutex_init(&state->open_mutex); - file->private_data = state; - dmabuf->trigger = 0; - /* allocate hardware channels */ - if (file->f_mode & FMODE_READ) { - if ((dmabuf->read_channel = - card->alloc_rec_pcm_channel(card)) == NULL) { - kfree(card->states[i]); - card->states[i] = NULL; - return -EBUSY; - } - dmabuf->trigger |= PCM_ENABLE_INPUT; - ali_set_adc_rate(state, 8000); - } - if (file->f_mode & FMODE_WRITE) { - if (codec_independent_spdif_locked > 0) { - if ((dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card)) == NULL) { - kfree(card->states[i]); - card->states[i] = NULL; - return -EBUSY; - } - dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; - ali_set_codecspdifout_rate(state, codec_independent_spdif_locked); //It must add - i_scr = inl(state->card->iobase + ALI_SCR); - if ((i_scr & 0x00300000) == 0x00100000) { - ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); - } else { - if ((i_scr & 0x00300000) == 0x00200000) { - ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); - } else { - if ((i_scr & 0x00300000) == 0x00300000) { - ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); - } else { - ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); - } - } - - } - } else { - if (controller_independent_spdif_locked > 0) { - if ((dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card)) == NULL) { - kfree(card->states[i]); - card->states[i] = NULL; - return -EBUSY; - } - dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; - ali_set_spdifout_rate(state, controller_independent_spdif_locked); - } else { - if ((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { - kfree(card->states[i]); - card->states[i] = NULL; - return -EBUSY; - } - /* Initialize to 8kHz? What if we don't support 8kHz? */ - /* Let's change this to check for S/PDIF stuff */ - - dmabuf->trigger |= PCM_ENABLE_OUTPUT; - if (codec_pcmout_share_spdif_locked) { - ali_set_dac_rate(state, codec_pcmout_share_spdif_locked); - ali_set_spdif_output(state, AC97_EA_SPSA_3_4, codec_pcmout_share_spdif_locked); - } else { - ali_set_dac_rate(state, 8000); - } - } - - } - } - - /* set default sample format. According to OSS Programmer's Guide /dev/dsp - should be default to unsigned 8-bits, mono, with sample rate 8kHz and - /dev/dspW will accept 16-bits sample, but we don't support those so we - set it immediately to stereo and 16bit, which is all we do support */ - dmabuf->fmt |= ALI5455_FMT_16BIT | ALI5455_FMT_STEREO; - dmabuf->ossfragsize = 0; - dmabuf->ossmaxfrags = 0; - dmabuf->subdivision = 0; - state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - outl(0x00000000, card->iobase + ALI_INTERRUPTCR); - outl(0x00000000, card->iobase + ALI_INTERRUPTSR); - return nonseekable_open(inode, file); -} - -static int ali_release(struct inode *inode, struct file *file) -{ - struct ali_state *state = (struct ali_state *) file->private_data; - struct ali_card *card = state->card; - struct dmabuf *dmabuf = &state->dmabuf; - unsigned long flags; - lock_kernel(); - - /* stop DMA state machine and free DMA buffers/channels */ - if (dmabuf->trigger & PCM_ENABLE_OUTPUT) - drain_dac(state, 0); - - if (dmabuf->trigger & SPDIF_ENABLE_OUTPUT) - drain_spdifout(state, 0); - - if (dmabuf->trigger & PCM_ENABLE_INPUT) - stop_adc(state); - - spin_lock_irqsave(&card->lock, flags); - dealloc_dmabuf(state); - if (file->f_mode & FMODE_WRITE) { - if (codec_independent_spdif_locked > 0) { - state->card->free_pcm_channel(state->card, dmabuf->codec_spdifout_channel->num); - } else { - if (controller_independent_spdif_locked > 0) - state->card->free_pcm_channel(state->card, - dmabuf->controller_spdifout_channel->num); - else state->card->free_pcm_channel(state->card, - dmabuf->write_channel->num); - } - } - if (file->f_mode & FMODE_READ) - state->card->free_pcm_channel(state->card, dmabuf->read_channel->num); - - state->card->states[state->virt] = NULL; - kfree(state); - spin_unlock_irqrestore(&card->lock, flags); - unlock_kernel(); - return 0; -} - -static /*const */ struct file_operations ali_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = ali_read, - .write = ali_write, - .poll = ali_poll, - .ioctl = ali_ioctl, - .mmap = ali_mmap, - .open = ali_open, - .release = ali_release, -}; - -/* Read AC97 codec registers */ -static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg) -{ - struct ali_card *card = dev->private_data; - int count1 = 100; - char val; - unsigned short int data = 0, count, addr1, addr2 = 0; - - spin_lock(&card->ac97_lock); - while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) - udelay(1); - - addr1 = reg; - reg |= 0x0080; - for (count = 0; count < 0x7f; count++) { - val = inb(card->iobase + ALI_CSPSR); - if (val & 0x08) - break; - } - if (count == 0x7f) - { - spin_unlock(&card->ac97_lock); - return -1; - } - outw(reg, (card->iobase + ALI_CPR) + 2); - for (count = 0; count < 0x7f; count++) { - val = inb(card->iobase + ALI_CSPSR); - if (val & 0x02) { - data = inw(card->iobase + ALI_SPR); - addr2 = inw((card->iobase + ALI_SPR) + 2); - break; - } - } - spin_unlock(&card->ac97_lock); - if (count == 0x7f) - return -1; - if (addr2 != addr1) - return -1; - return ((u16) data); -} - -/* write ac97 codec register */ - -static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) -{ - struct ali_card *card = dev->private_data; - int count1 = 100; - char val; - unsigned short int count; - - spin_lock(&card->ac97_lock); - while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) - udelay(1); - - for (count = 0; count < 0x7f; count++) { - val = inb(card->iobase + ALI_CSPSR); - if (val & 0x08) - break; - } - if (count == 0x7f) { - printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); - spin_unlock(&card->ac97_lock); - return; - } - outw(data, (card->iobase + ALI_CPR)); - outb(reg, (card->iobase + ALI_CPR) + 2); - for (count = 0; count < 0x7f; count++) { - val = inb(card->iobase + ALI_CSPSR); - if (val & 0x01) - break; - } - spin_unlock(&card->ac97_lock); - if (count == 0x7f) - printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); - return; -} - -/* OSS /dev/mixer file operation methods */ - -static int ali_open_mixdev(struct inode *inode, struct file *file) -{ - int i; - int minor = iminor(inode); - struct ali_card *card = devs; - for (card = devs; card != NULL; card = card->next) { - /* - * If we are initializing and then fail, card could go - * away unuexpectedly while we are in the for() loop. - * So, check for card on each iteration before we check - * for card->initializing to avoid a possible oops. - * This usually only matters for times when the driver is - * autoloaded by kmod. - */ - for (i = 0; i < 50 && card && card->initializing; i++) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 20); - } - for (i = 0; i < NR_AC97 && card && !card->initializing; i++) - if (card->ac97_codec[i] != NULL - && card->ac97_codec[i]->dev_mixer == minor) { - file->private_data = card->ac97_codec[i]; - return nonseekable_open(inode, file); - } - } - return -ENODEV; -} - -static int ali_ioctl_mixdev(struct inode *inode, - struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ac97_codec *codec = (struct ac97_codec *) file->private_data; - return codec->mixer_ioctl(codec, cmd, arg); -} - -static /*const */ struct file_operations ali_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = ali_ioctl_mixdev, - .open = ali_open_mixdev, -}; - -/* AC97 codec initialisation. These small functions exist so we don't - duplicate code between module init and apm resume */ - -static inline int ali_ac97_exists(struct ali_card *card, int ac97_number) -{ - unsigned int i = 1; - u32 reg = inl(card->iobase + ALI_RTSR); - if (ac97_number) { - while (i < 100) { - - reg = inl(card->iobase + ALI_RTSR); - if (reg & 0x40) { - break; - } else { - outl(reg | 0x00000040, - card->iobase + 0x34); - udelay(1); - } - i++; - } - - } else { - while (i < 100) { - reg = inl(card->iobase + ALI_RTSR); - if (reg & 0x80) { - break; - } else { - outl(reg | 0x00000080, - card->iobase + 0x34); - udelay(1); - } - i++; - } - } - - if (ac97_number) - return reg & 0x40; - else - return reg & 0x80; -} - -static inline int ali_ac97_enable_variable_rate(struct ac97_codec *codec) -{ - ali_ac97_set(codec, AC97_EXTENDED_STATUS, 9); - ali_ac97_set(codec, AC97_EXTENDED_STATUS, ali_ac97_get(codec, AC97_EXTENDED_STATUS) | 0xE800); - return (ali_ac97_get(codec, AC97_EXTENDED_STATUS) & 1); -} - - -static int ali_ac97_probe_and_powerup(struct ali_card *card, struct ac97_codec *codec) -{ - /* Returns 0 on failure */ - int i; - u16 addr; - if (ac97_probe_codec(codec) == 0) - return 0; - /* ac97_probe_codec is success ,then begin to init codec */ - ali_ac97_set(codec, AC97_RESET, 0xffff); - if (card->channel[0].used == 1) { - ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); - ali_ac97_set(codec, AC97_LINEIN_VOL, 0x0808); - ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); - } - - if (card->channel[2].used == 1) //if MICin then init codec - { - ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); - ali_ac97_set(codec, AC97_MIC_VOL, 0x8808); - ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); - ali_ac97_set(codec, AC97_RECORD_GAIN_MIC, 0x0000); - } - - ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, 0x0000); - ali_ac97_set(codec, AC97_HEADPHONE_VOL, 0x0000); - ali_ac97_set(codec, AC97_PCMOUT_VOL, 0x0000); - ali_ac97_set(codec, AC97_CD_VOL, 0x0808); - ali_ac97_set(codec, AC97_VIDEO_VOL, 0x0808); - ali_ac97_set(codec, AC97_AUX_VOL, 0x0808); - ali_ac97_set(codec, AC97_PHONE_VOL, 0x8048); - ali_ac97_set(codec, AC97_PCBEEP_VOL, 0x0000); - ali_ac97_set(codec, AC97_GENERAL_PURPOSE, AC97_GP_MIX); - ali_ac97_set(codec, AC97_MASTER_VOL_MONO, 0x0000); - ali_ac97_set(codec, 0x38, 0x0000); - addr = ali_ac97_get(codec, 0x2a); - ali_ac97_set(codec, 0x2a, addr | 0x0001); - addr = ali_ac97_get(codec, 0x2a); - addr = ali_ac97_get(codec, 0x28); - ali_ac97_set(codec, 0x2c, 0xbb80); - addr = ali_ac97_get(codec, 0x2c); - /* power it all up */ - ali_ac97_set(codec, AC97_POWER_CONTROL, - ali_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); - /* wait for analog ready */ - for (i = 10; i && ((ali_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 20); - } - /* FIXME !! */ - i++; - return i; -} - - -/* I clone ali5455(2.4.7 ) not clone i810_audio(2.4.18) */ - -static int ali_reset_5455(struct ali_card *card) -{ - outl(0x80000003, card->iobase + ALI_SCR); - outl(0x83838383, card->iobase + ALI_FIFOCR1); - outl(0x83838383, card->iobase + ALI_FIFOCR2); - if (controller_pcmout_share_spdif_locked > 0) { - outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), - card->iobase + ALI_SPDIFICS); - outl(0x0408000a, card->iobase + ALI_INTERFACECR); - } else { - if (codec_independent_spdif_locked > 0) { - outl((inl(card->iobase + ALI_SCR) | 0x00100000), card->iobase + ALI_SCR); // now I select slot 7 & 8 - outl(0x00200000, card->iobase + ALI_INTERFACECR); //enable codec independent spdifout - } else - outl(0x04080002, card->iobase + ALI_INTERFACECR); - } - - outl(0x00000000, card->iobase + ALI_INTERRUPTCR); - outl(0x00000000, card->iobase + ALI_INTERRUPTSR); - if (controller_independent_spdif_locked > 0) - outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), - card->iobase + ALI_SPDIFICS); - return 1; -} - - -static int ali_ac97_random_init_stuff(struct ali_card - *card) -{ - u32 reg = inl(card->iobase + ALI_SCR); - int i = 0; - reg = inl(card->iobase + ALI_SCR); - if ((reg & 2) == 0) /* Cold required */ - reg |= 2; - else - reg |= 1; /* Warm */ - reg &= ~0x80000000; /* ACLink on */ - outl(reg, card->iobase + ALI_SCR); - - while (i < 10) { - if ((inl(card->iobase + 0x18) & (1 << 1)) == 0) - break; - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ / 20); - i++; - } - if (i == 10) { - printk(KERN_ERR "ali_audio: AC'97 reset failed.\n"); - return 0; - } - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 2); - return 1; -} - -/* AC97 codec initialisation. */ - -static int __devinit ali_ac97_init(struct ali_card *card) -{ - int num_ac97 = 0; - int total_channels = 0; - struct ac97_codec *codec; - u16 eid; - - if (!ali_ac97_random_init_stuff(card)) - return 0; - - /* Number of channels supported */ - /* What about the codec? Just because the ICH supports */ - /* multiple channels doesn't mean the codec does. */ - /* we'll have to modify this in the codec section below */ - /* to reflect what the codec has. */ - /* ICH and ICH0 only support 2 channels so don't bother */ - /* to check.... */ - inl(card->iobase + ALI_CPR); - card->channels = 2; - - for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - - /* Assume codec isn't available until we go through the - * gauntlet below */ - card->ac97_codec[num_ac97] = NULL; - /* The ICH programmer's reference says you should */ - /* check the ready status before probing. So we chk */ - /* What do we do if it's not ready? Wait and try */ - /* again, or abort? */ - if (!ali_ac97_exists(card, num_ac97)) { - if (num_ac97 == 0) - printk(KERN_ERR "ali_audio: Primary codec not ready.\n"); - break; - } - - if ((codec = ac97_alloc_codec()) == NULL) - return -ENOMEM; - /* initialize some basic codec information, other fields will be filled - in ac97_probe_codec */ - codec->private_data = card; - codec->id = num_ac97; - codec->codec_read = ali_ac97_get; - codec->codec_write = ali_ac97_set; - if (!ali_ac97_probe_and_powerup(card, codec)) { - printk(KERN_ERR "ali_audio: timed out waiting for codec %d analog ready", - num_ac97); - kfree(codec); - break; /* it didn't work */ - } - - /* Store state information about S/PDIF transmitter */ - card->ac97_status = 0; - /* Don't attempt to get eid until powerup is complete */ - eid = ali_ac97_get(codec, AC97_EXTENDED_ID); - if (eid == 0xFFFF) { - printk(KERN_ERR "ali_audio: no codec attached ?\n"); - kfree(codec); - break; - } - - card->ac97_features = eid; - /* Now check the codec for useful features to make up for - the dumbness of the ali5455 hardware engine */ - if (!(eid & 0x0001)) - printk(KERN_WARNING - "ali_audio: only 48Khz playback available.\n"); - else { - if (!ali_ac97_enable_variable_rate(codec)) { - printk(KERN_WARNING - "ali_audio: Codec refused to allow VRA, using 48Khz only.\n"); - card->ac97_features &= ~1; - } - } - - /* Determine how many channels the codec(s) support */ - /* - The primary codec always supports 2 */ - /* - If the codec supports AMAP, surround DACs will */ - /* automaticlly get assigned to slots. */ - /* * Check for surround DACs and increment if */ - /* found. */ - /* - Else check if the codec is revision 2.2 */ - /* * If surround DACs exist, assign them to slots */ - /* and increment channel count. */ - - /* All of this only applies to ICH2 and above. ICH */ - /* and ICH0 only support 2 channels. ICH2 will only */ - /* support multiple codecs in a "split audio" config. */ - /* as described above. */ - - /* TODO: Remove all the debugging messages! */ - - if ((eid & 0xc000) == 0) /* primary codec */ - total_channels += 2; - if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) { - printk(KERN_ERR "ali_audio: couldn't register mixer!\n"); - kfree(codec); - break; - } - card->ac97_codec[num_ac97] = codec; - } - /* pick the minimum of channels supported by ICHx or codec(s) */ - card->channels = (card->channels > total_channels) ? total_channels : card->channels; - return num_ac97; -} - -static void __devinit ali_configure_clocking(void) -{ - struct ali_card *card; - struct ali_state *state; - struct dmabuf *dmabuf; - unsigned int i, offset, new_offset; - unsigned long flags; - card = devs; - - /* We could try to set the clocking for multiple cards, but can you even have - * more than one ali in a machine? Besides, clocking is global, so unless - * someone actually thinks more than one ali in a machine is possible and - * decides to rewrite that little bit, setting the rate for more than one card - * is a waste of time. - */ - if (card != NULL) { - state = card->states[0] = (struct ali_state *) - kmalloc(sizeof(struct ali_state), GFP_KERNEL); - if (state == NULL) - return; - memset(state, 0, sizeof(struct ali_state)); - dmabuf = &state->dmabuf; - dmabuf->write_channel = card->alloc_pcm_channel(card); - state->virt = 0; - state->card = card; - state->magic = ALI5455_STATE_MAGIC; - init_waitqueue_head(&dmabuf->wait); - mutex_init(&state->open_mutex); - dmabuf->fmt = ALI5455_FMT_STEREO | ALI5455_FMT_16BIT; - dmabuf->trigger = PCM_ENABLE_OUTPUT; - ali_set_dac_rate(state, 48000); - if (prog_dmabuf(state, 0) != 0) - goto config_out_nodmabuf; - - if (dmabuf->dmasize < 16384) - goto config_out; - - dmabuf->count = dmabuf->dmasize; - outb(31, card->iobase + dmabuf->write_channel->port + OFF_LVI); - - local_irq_save(flags); - start_dac(state); - offset = ali_get_dma_addr(state, 0); - mdelay(50); - new_offset = ali_get_dma_addr(state, 0); - stop_dac(state); - - outb(2, card->iobase + dmabuf->write_channel->port + OFF_CR); - local_irq_restore(flags); - - i = new_offset - offset; - - if (i == 0) - goto config_out; - i = i / 4 * 20; - if (i > 48500 || i < 47500) { - clocking = clocking * clocking / i; - } -config_out: - dealloc_dmabuf(state); -config_out_nodmabuf: - state->card->free_pcm_channel(state->card, state->dmabuf. write_channel->num); - kfree(state); - card->states[0] = NULL; - } -} - -/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered - until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ - -static int __devinit ali_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) -{ - struct ali_card *card; - if (pci_enable_device(pci_dev)) - return -EIO; - if (pci_set_dma_mask(pci_dev, ALI5455_DMA_MASK)) { - printk(KERN_ERR "ali5455: architecture does not support" - " 32bit PCI busmaster DMA\n"); - return -ENODEV; - } - - if ((card = kmalloc(sizeof(struct ali_card), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "ali_audio: out of memory\n"); - return -ENOMEM; - } - memset(card, 0, sizeof(*card)); - card->initializing = 1; - card->iobase = pci_resource_start(pci_dev, 0); - card->pci_dev = pci_dev; - card->pci_id = pci_id->device; - card->irq = pci_dev->irq; - card->next = devs; - card->magic = ALI5455_CARD_MAGIC; -#ifdef CONFIG_PM - card->pm_suspended = 0; -#endif - spin_lock_init(&card->lock); - spin_lock_init(&card->ac97_lock); - devs = card; - pci_set_master(pci_dev); - printk(KERN_INFO "ali: %s found at IO 0x%04lx, IRQ %d\n", - card_names[pci_id->driver_data], card->iobase, card->irq); - card->alloc_pcm_channel = ali_alloc_pcm_channel; - card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; - card->alloc_rec_mic_channel = ali_alloc_rec_mic_channel; - card->alloc_codec_spdifout_channel = ali_alloc_codec_spdifout_channel; - card->alloc_controller_spdifout_channel = ali_alloc_controller_spdifout_channel; - card->free_pcm_channel = ali_free_pcm_channel; - card->channel[0].offset = 0; - card->channel[0].port = 0x40; - card->channel[0].num = 0; - card->channel[1].offset = 0; - card->channel[1].port = 0x50; - card->channel[1].num = 1; - card->channel[2].offset = 0; - card->channel[2].port = 0x60; - card->channel[2].num = 2; - card->channel[3].offset = 0; - card->channel[3].port = 0x70; - card->channel[3].num = 3; - card->channel[4].offset = 0; - card->channel[4].port = 0xb0; - card->channel[4].num = 4; - /* claim our iospace and irq */ - request_region(card->iobase, 256, card_names[pci_id->driver_data]); - if (request_irq(card->irq, &ali_interrupt, IRQF_SHARED, - card_names[pci_id->driver_data], card)) { - printk(KERN_ERR "ali_audio: unable to allocate irq %d\n", - card->irq); - release_region(card->iobase, 256); - kfree(card); - return -ENODEV; - } - - if (ali_reset_5455(card) <= 0) { - unregister_sound_dsp(card->dev_audio); - release_region(card->iobase, 256); - free_irq(card->irq, card); - kfree(card); - return -ENODEV; - } - - /* initialize AC97 codec and register /dev/mixer */ - if (ali_ac97_init(card) < 0) { - release_region(card->iobase, 256); - free_irq(card->irq, card); - kfree(card); - return -ENODEV; - } - - pci_set_drvdata(pci_dev, card); - - if (clocking == 0) { - clocking = 48000; - ali_configure_clocking(); - } - - /* register /dev/dsp */ - if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) { - int i; - printk(KERN_ERR"ali_audio: couldn't register DSP device!\n"); - release_region(card->iobase, 256); - free_irq(card->irq, card); - for (i = 0; i < NR_AC97; i++) - if (card->ac97_codec[i] != NULL) { - unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); - kfree(card->ac97_codec[i]); - } - kfree(card); - return -ENODEV; - } - card->initializing = 0; - return 0; -} - -static void __devexit ali_remove(struct pci_dev *pci_dev) -{ - int i; - struct ali_card *card = pci_get_drvdata(pci_dev); - /* free hardware resources */ - free_irq(card->irq, devs); - release_region(card->iobase, 256); - /* unregister audio devices */ - for (i = 0; i < NR_AC97; i++) - if (card->ac97_codec[i] != NULL) { - unregister_sound_mixer(card->ac97_codec[i]-> - dev_mixer); - ac97_release_codec(card->ac97_codec[i]); - card->ac97_codec[i] = NULL; - } - unregister_sound_dsp(card->dev_audio); - kfree(card); -} - -#ifdef CONFIG_PM -static int ali_pm_suspend(struct pci_dev *dev, pm_message_t pm_state) -{ - struct ali_card *card = pci_get_drvdata(dev); - struct ali_state *state; - unsigned long flags; - struct dmabuf *dmabuf; - int i, num_ac97; - - if (!card) - return 0; - spin_lock_irqsave(&card->lock, flags); - card->pm_suspended = 1; - for (i = 0; i < NR_HW_CH; i++) { - state = card->states[i]; - if (!state) - continue; - /* this happens only if there are open files */ - dmabuf = &state->dmabuf; - if (dmabuf->enable & DAC_RUNNING || - (dmabuf->count - && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) { - state->pm_saved_dac_rate = dmabuf->rate; - stop_dac(state); - } else { - state->pm_saved_dac_rate = 0; - } - if (dmabuf->enable & ADC_RUNNING) { - state->pm_saved_adc_rate = dmabuf->rate; - stop_adc(state); - } else { - state->pm_saved_adc_rate = 0; - } - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr = 0; - dmabuf->count = dmabuf->total_bytes = 0; - } - - spin_unlock_irqrestore(&card->lock, flags); - /* save mixer settings */ - for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - struct ac97_codec *codec = card->ac97_codec[num_ac97]; - if (!codec) - continue; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if ((supported_mixer(codec, i)) && (codec->read_mixer)) { - card->pm_saved_mixer_settings[i][num_ac97] = codec->read_mixer(codec, i); - } - } - } - pci_save_state(dev); /* XXX do we need this? */ - pci_disable_device(dev); /* disable busmastering */ - pci_set_power_state(dev, 3); /* Zzz. */ - return 0; -} - - -static int ali_pm_resume(struct pci_dev *dev) -{ - int num_ac97, i = 0; - struct ali_card *card = pci_get_drvdata(dev); - pci_enable_device(dev); - pci_restore_state(dev); - /* observation of a toshiba portege 3440ct suggests that the - hardware has to be more or less completely reinitialized from - scratch after an apm suspend. Works For Me. -dan */ - ali_ac97_random_init_stuff(card); - for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { - struct ac97_codec *codec = card->ac97_codec[num_ac97]; - /* check they haven't stolen the hardware while we were - away */ - if (!codec || !ali_ac97_exists(card, num_ac97)) { - if (num_ac97) - continue; - else - BUG(); - } - if (!ali_ac97_probe_and_powerup(card, codec)) - BUG(); - if ((card->ac97_features & 0x0001)) { - /* at probe time we found we could do variable - rates, but APM suspend has made it forget - its magical powers */ - if (!ali_ac97_enable_variable_rate(codec)) - BUG(); - } - /* we lost our mixer settings, so restore them */ - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (supported_mixer(codec, i)) { - int val = card->pm_saved_mixer_settings[i][num_ac97]; - codec->mixer_state[i] = val; - codec->write_mixer(codec, i, - (val & 0xff), - ((val >> 8) & 0xff)); - } - } - } - - /* we need to restore the sample rate from whatever it was */ - for (i = 0; i < NR_HW_CH; i++) { - struct ali_state *state = card->states[i]; - if (state) { - if (state->pm_saved_adc_rate) - ali_set_adc_rate(state, state->pm_saved_adc_rate); - if (state->pm_saved_dac_rate) - ali_set_dac_rate(state, state->pm_saved_dac_rate); - } - } - - card->pm_suspended = 0; - /* any processes that were reading/writing during the suspend - probably ended up here */ - for (i = 0; i < NR_HW_CH; i++) { - struct ali_state *state = card->states[i]; - if (state) - wake_up(&state->dmabuf.wait); - } - return 0; -} -#endif /* CONFIG_PM */ - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("ALI 5455 audio support"); -MODULE_LICENSE("GPL"); -module_param(clocking, int, 0); -/* FIXME: bool? */ -module_param(strict_clocking, uint, 0); -module_param(codec_pcmout_share_spdif_locked, uint, 0); -module_param(codec_independent_spdif_locked, uint, 0); -module_param(controller_pcmout_share_spdif_locked, uint, 0); -module_param(controller_independent_spdif_locked, uint, 0); -#define ALI5455_MODULE_NAME "ali5455" -static struct pci_driver ali_pci_driver = { - .name = ALI5455_MODULE_NAME, - .id_table = ali_pci_tbl, - .probe = ali_probe, - .remove = __devexit_p(ali_remove), -#ifdef CONFIG_PM - .suspend = ali_pm_suspend, - .resume = ali_pm_resume, -#endif /* CONFIG_PM */ -}; - -static int __init ali_init_module(void) -{ - printk(KERN_INFO "ALI 5455 + AC97 Audio, version " - DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); - - if (codec_independent_spdif_locked > 0) { - if (codec_independent_spdif_locked == 32000 - || codec_independent_spdif_locked == 44100 - || codec_independent_spdif_locked == 48000) { - printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_independent_spdif_locked); - } else { - printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); - codec_independent_spdif_locked = 0; - } - } - if (controller_independent_spdif_locked > 0) { - if (controller_independent_spdif_locked == 32000 - || controller_independent_spdif_locked == 44100 - || controller_independent_spdif_locked == 48000) { - printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", controller_independent_spdif_locked); - } else { - printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); - controller_independent_spdif_locked = 0; - } - } - - if (codec_pcmout_share_spdif_locked > 0) { - if (codec_pcmout_share_spdif_locked == 32000 - || codec_pcmout_share_spdif_locked == 44100 - || codec_pcmout_share_spdif_locked == 48000) { - printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_pcmout_share_spdif_locked); - } else { - printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); - codec_pcmout_share_spdif_locked = 0; - } - } - if (controller_pcmout_share_spdif_locked > 0) { - if (controller_pcmout_share_spdif_locked == 32000 - || controller_pcmout_share_spdif_locked == 44100 - || controller_pcmout_share_spdif_locked == 48000) { - printk(KERN_INFO "ali_audio: Enabling controller S/PDIF at sample rate %dHz.\n", controller_pcmout_share_spdif_locked); - } else { - printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); - controller_pcmout_share_spdif_locked = 0; - } - } - return pci_register_driver(&ali_pci_driver); -} - -static void __exit ali_cleanup_module(void) -{ - pci_unregister_driver(&ali_pci_driver); -} - -module_init(ali_init_module); -module_exit(ali_cleanup_module); -/* -Local Variables: -c-basic-offset: 8 -End: -*/ diff --git a/sound/oss/au1000.c b/sound/oss/au1000.c deleted file mode 100644 index e379623145..0000000000 --- a/sound/oss/au1000.c +++ /dev/null @@ -1,2216 +0,0 @@ -/* - * au1000.c -- Sound driver for Alchemy Au1000 MIPS Internet Edge - * Processor. - * - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * stevel@mvista.com or source@mvista.com - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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. - * - * - * Module command line parameters: - * - * Supported devices: - * /dev/dsp standard OSS /dev/dsp device - * /dev/mixer standard OSS /dev/mixer device - * - * Notes: - * - * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are - * taken, slightly modified or not at all, from the ES1371 driver, - * so refer to the credits in es1371.c for those. The rest of the - * code (probe, open, read, write, the ISR, etc.) is new. - * - * Revision history - * 06.27.2001 Initial version - * 03.20.2002 Added mutex locks around read/write methods, to prevent - * simultaneous access on SMP or preemptible kernels. Also - * removed the counter/pointer fragment aligning at the end - * of read/write methods [stevel]. - * 03.21.2002 Add support for coherent DMA on the audio read/write DMA - * channels [stevel]. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS -#undef AU1000_DEBUG -#undef AU1000_VERBOSE_DEBUG - -#define AU1000_MODULE_NAME "Au1000 audio" -#define PFX AU1000_MODULE_NAME - -#ifdef AU1000_DEBUG -#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) -#else -#define dbg(format, arg...) do {} while (0) -#endif -#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) -#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) -#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) - - -/* misc stuff */ -#define POLL_COUNT 0x5000 -#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC) - -/* Boot options */ -static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it -module_param(vra, bool, 0); -MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); - - -/* --------------------------------------------------------------------- */ - -struct au1000_state { - /* soundcore stuff */ - int dev_audio; - -#ifdef AU1000_DEBUG - /* debug /proc entry */ - struct proc_dir_entry *ps; - struct proc_dir_entry *ac97_ps; -#endif /* AU1000_DEBUG */ - - struct ac97_codec codec; - unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register" - unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID" - int no_vra; // do not use VRA - - spinlock_t lock; - struct mutex open_mutex; - struct mutex sem; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - unsigned int dmanr; // DMA Channel number - unsigned sample_rate; // Hz - unsigned src_factor; // SRC interp/decimation (no vra) - unsigned sample_size; // 8 or 16 - int num_channels; // 1 = mono, 2 = stereo, 4, 6 - int dma_bytes_per_sample;// DMA bytes per audio sample frame - int user_bytes_per_sample;// User bytes per audio sample frame - int cnt_factor; // user-to-DMA bytes per audio - // sample frame - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; // # of DMA fragments in DMA buffer - unsigned fragshift; - void *nextIn; // ptr to next-in to DMA buffer - void *nextOut;// ptr to next-out from DMA buffer - int count; // current byte count in DMA buffer - unsigned total_bytes; // total bytes written or read - unsigned error; // over/underrun - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; // user perception of fragment size - unsigned dma_fragsize; // DMA (real) fragment size - unsigned dmasize; // Total DMA buffer size - // (mult. of DMA fragsize) - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned stopped:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac , dma_adc; -} au1000_state; - -/* --------------------------------------------------------------------- */ - - -static inline unsigned ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -/* --------------------------------------------------------------------- */ - -static void au1000_delay(int msec) -{ - unsigned long tmo; - signed long tmo2; - - if (in_interrupt()) - return; - - tmo = jiffies + (msec * HZ) / 1000; - for (;;) { - tmo2 = tmo - jiffies; - if (tmo2 <= 0) - break; - schedule_timeout(tmo2); - } -} - - -/* --------------------------------------------------------------------- */ - -static u16 rdcodec(struct ac97_codec *codec, u8 addr) -{ - struct au1000_state *s = (struct au1000_state *)codec->private_data; - unsigned long flags; - u32 cmd; - u16 data; - int i; - - spin_lock_irqsave(&s->lock, flags); - - for (i = 0; i < POLL_COUNT; i++) - if (!(au_readl(AC97C_STATUS) & AC97C_CP)) - break; - if (i == POLL_COUNT) - err("rdcodec: codec cmd pending expired!"); - - cmd = (u32) addr & AC97C_INDEX_MASK; - cmd |= AC97C_READ; // read command - au_writel(cmd, AC97C_CMD); - - /* now wait for the data */ - for (i = 0; i < POLL_COUNT; i++) - if (!(au_readl(AC97C_STATUS) & AC97C_CP)) - break; - if (i == POLL_COUNT) { - err("rdcodec: read poll expired!"); - return 0; - } - - data = au_readl(AC97C_CMD) & 0xffff; - - spin_unlock_irqrestore(&s->lock, flags); - - return data; -} - - -static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) -{ - struct au1000_state *s = (struct au1000_state *)codec->private_data; - unsigned long flags; - u32 cmd; - int i; - - spin_lock_irqsave(&s->lock, flags); - - for (i = 0; i < POLL_COUNT; i++) - if (!(au_readl(AC97C_STATUS) & AC97C_CP)) - break; - if (i == POLL_COUNT) - err("wrcodec: codec cmd pending expired!"); - - cmd = (u32) addr & AC97C_INDEX_MASK; - cmd &= ~AC97C_READ; // write command - cmd |= ((u32) data << AC97C_WD_BIT); // OR in the data word - au_writel(cmd, AC97C_CMD); - - spin_unlock_irqrestore(&s->lock, flags); -} - -static void waitcodec(struct ac97_codec *codec) -{ - u16 temp; - int i; - - /* codec_wait is used to wait for a ready state after - an AC97C_RESET. */ - au1000_delay(10); - - // first poll the CODEC_READY tag bit - for (i = 0; i < POLL_COUNT; i++) - if (au_readl(AC97C_STATUS) & AC97C_READY) - break; - if (i == POLL_COUNT) { - err("waitcodec: CODEC_READY poll expired!"); - return; - } - // get AC'97 powerdown control/status register - temp = rdcodec(codec, AC97_POWER_CONTROL); - - // If anything is powered down, power'em up - if (temp & 0x7f00) { - // Power on - wrcodec(codec, AC97_POWER_CONTROL, 0); - au1000_delay(100); - // Reread - temp = rdcodec(codec, AC97_POWER_CONTROL); - } - - // Check if Codec REF,ANL,DAC,ADC ready - if ((temp & 0x7f0f) != 0x000f) - err("codec reg 26 status (0x%x) not ready!!", temp); -} - - -/* --------------------------------------------------------------------- */ - -/* stop the ADC before calling */ -static void set_adc_rate(struct au1000_state *s, unsigned rate) -{ - struct dmabuf *adc = &s->dma_adc; - struct dmabuf *dac = &s->dma_dac; - unsigned adc_rate, dac_rate; - u16 ac97_extstat; - - if (s->no_vra) { - // calc SRC factor - adc->src_factor = ((96000 / rate) + 1) >> 1; - adc->sample_rate = 48000 / adc->src_factor; - return; - } - - adc->src_factor = 1; - - ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS); - - rate = rate > 48000 ? 48000 : rate; - - // enable VRA - wrcodec(&s->codec, AC97_EXTENDED_STATUS, - ac97_extstat | AC97_EXTSTAT_VRA); - // now write the sample rate - wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate); - // read it back for actual supported rate - adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE); - -#ifdef AU1000_VERBOSE_DEBUG - dbg("%s: set to %d Hz", __FUNCTION__, adc_rate); -#endif - - // some codec's don't allow unequal DAC and ADC rates, in which case - // writing one rate reg actually changes both. - dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE); - if (dac->num_channels > 2) - wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate); - if (dac->num_channels > 4) - wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate); - - adc->sample_rate = adc_rate; - dac->sample_rate = dac_rate; -} - -/* stop the DAC before calling */ -static void set_dac_rate(struct au1000_state *s, unsigned rate) -{ - struct dmabuf *dac = &s->dma_dac; - struct dmabuf *adc = &s->dma_adc; - unsigned adc_rate, dac_rate; - u16 ac97_extstat; - - if (s->no_vra) { - // calc SRC factor - dac->src_factor = ((96000 / rate) + 1) >> 1; - dac->sample_rate = 48000 / dac->src_factor; - return; - } - - dac->src_factor = 1; - - ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS); - - rate = rate > 48000 ? 48000 : rate; - - // enable VRA - wrcodec(&s->codec, AC97_EXTENDED_STATUS, - ac97_extstat | AC97_EXTSTAT_VRA); - // now write the sample rate - wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate); - // I don't support different sample rates for multichannel, - // so make these channels the same. - if (dac->num_channels > 2) - wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate); - if (dac->num_channels > 4) - wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate); - // read it back for actual supported rate - dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE); - -#ifdef AU1000_VERBOSE_DEBUG - dbg("%s: set to %d Hz", __FUNCTION__, dac_rate); -#endif - - // some codec's don't allow unequal DAC and ADC rates, in which case - // writing one rate reg actually changes both. - adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE); - - dac->sample_rate = dac_rate; - adc->sample_rate = adc_rate; -} - -static void stop_dac(struct au1000_state *s) -{ - struct dmabuf *db = &s->dma_dac; - unsigned long flags; - - if (db->stopped) - return; - - spin_lock_irqsave(&s->lock, flags); - - disable_dma(db->dmanr); - - db->stopped = 1; - - spin_unlock_irqrestore(&s->lock, flags); -} - -static void stop_adc(struct au1000_state *s) -{ - struct dmabuf *db = &s->dma_adc; - unsigned long flags; - - if (db->stopped) - return; - - spin_lock_irqsave(&s->lock, flags); - - disable_dma(db->dmanr); - - db->stopped = 1; - - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void set_xmit_slots(int num_channels) -{ - u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_XMIT_SLOTS_MASK; - - switch (num_channels) { - case 1: // mono - case 2: // stereo, slots 3,4 - ac97_config |= (0x3 << AC97C_XMIT_SLOTS_BIT); - break; - case 4: // stereo with surround, slots 3,4,7,8 - ac97_config |= (0x33 << AC97C_XMIT_SLOTS_BIT); - break; - case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9 - ac97_config |= (0x7b << AC97C_XMIT_SLOTS_BIT); - break; - } - - au_writel(ac97_config, AC97C_CONFIG); -} - -static void set_recv_slots(int num_channels) -{ - u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_RECV_SLOTS_MASK; - - /* - * Always enable slots 3 and 4 (stereo). Slot 6 is - * optional Mic ADC, which I don't support yet. - */ - ac97_config |= (0x3 << AC97C_RECV_SLOTS_BIT); - - au_writel(ac97_config, AC97C_CONFIG); -} - -static void start_dac(struct au1000_state *s) -{ - struct dmabuf *db = &s->dma_dac; - unsigned long flags; - unsigned long buf1, buf2; - - if (!db->stopped) - return; - - spin_lock_irqsave(&s->lock, flags); - - au_readl(AC97C_STATUS); // read status to clear sticky bits - - // reset Buffer 1 and 2 pointers to nextOut and nextOut+dma_fragsize - buf1 = virt_to_phys(db->nextOut); - buf2 = buf1 + db->dma_fragsize; - if (buf2 >= db->dmaaddr + db->dmasize) - buf2 -= db->dmasize; - - set_xmit_slots(db->num_channels); - - init_dma(db->dmanr); - if (get_dma_active_buffer(db->dmanr) == 0) { - clear_dma_done0(db->dmanr); // clear DMA done bit - set_dma_addr0(db->dmanr, buf1); - set_dma_addr1(db->dmanr, buf2); - } else { - clear_dma_done1(db->dmanr); // clear DMA done bit - set_dma_addr1(db->dmanr, buf1); - set_dma_addr0(db->dmanr, buf2); - } - set_dma_count(db->dmanr, db->dma_fragsize>>1); - enable_dma_buffers(db->dmanr); - - start_dma(db->dmanr); - -#ifdef AU1000_VERBOSE_DEBUG - dump_au1000_dma_channel(db->dmanr); -#endif - - db->stopped = 0; - - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct au1000_state *s) -{ - struct dmabuf *db = &s->dma_adc; - unsigned long flags; - unsigned long buf1, buf2; - - if (!db->stopped) - return; - - spin_lock_irqsave(&s->lock, flags); - - au_readl(AC97C_STATUS); // read status to clear sticky bits - - // reset Buffer 1 and 2 pointers to nextIn and nextIn+dma_fragsize - buf1 = virt_to_phys(db->nextIn); - buf2 = buf1 + db->dma_fragsize; - if (buf2 >= db->dmaaddr + db->dmasize) - buf2 -= db->dmasize; - - set_recv_slots(db->num_channels); - - init_dma(db->dmanr); - if (get_dma_active_buffer(db->dmanr) == 0) { - clear_dma_done0(db->dmanr); // clear DMA done bit - set_dma_addr0(db->dmanr, buf1); - set_dma_addr1(db->dmanr, buf2); - } else { - clear_dma_done1(db->dmanr); // clear DMA done bit - set_dma_addr1(db->dmanr, buf1); - set_dma_addr0(db->dmanr, buf2); - } - set_dma_count(db->dmanr, db->dma_fragsize>>1); - enable_dma_buffers(db->dmanr); - - start_dma(db->dmanr); - -#ifdef AU1000_VERBOSE_DEBUG - dump_au1000_dma_channel(db->dmanr); -#endif - - db->stopped = 0; - - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db) -{ - struct page *page, *pend; - - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + - (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - dma_free_noncoherent(NULL, - PAGE_SIZE << db->buforder, - db->rawbuf, - db->dmaaddr); - } - db->rawbuf = db->nextIn = db->nextOut = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct au1000_state *s, struct dmabuf *db) -{ - int order; - unsigned user_bytes_per_sec; - unsigned bufs; - struct page *page, *pend; - unsigned rate = db->sample_rate; - - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; - order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = dma_alloc_noncoherent(NULL, - PAGE_SIZE << order, - &db->dmaaddr, - 0))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - /* now mark the pages as reserved; - otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + - (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - - db->cnt_factor = 1; - if (db->sample_size == 8) - db->cnt_factor *= 2; - if (db->num_channels == 1) - db->cnt_factor *= 2; - db->cnt_factor *= db->src_factor; - - db->count = 0; - db->nextIn = db->nextOut = db->rawbuf; - - db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels; - db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ? - 2 : db->num_channels); - - user_bytes_per_sec = rate * db->user_bytes_per_sample; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < user_bytes_per_sec) - db->fragshift = ld2(user_bytes_per_sec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(user_bytes_per_sec / 100 / - (db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - - db->fragsize = 1 << db->fragshift; - db->dma_fragsize = db->fragsize * db->cnt_factor; - db->numfrag = bufs / db->dma_fragsize; - - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->fragsize = 1 << db->fragshift; - db->dma_fragsize = db->fragsize * db->cnt_factor; - db->numfrag = bufs / db->dma_fragsize; - } - - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - - db->dmasize = db->dma_fragsize * db->numfrag; - memset(db->rawbuf, 0, bufs); - -#ifdef AU1000_VERBOSE_DEBUG - dbg("rate=%d, samplesize=%d, channels=%d", - rate, db->sample_size, db->num_channels); - dbg("fragsize=%d, cnt_factor=%d, dma_fragsize=%d", - db->fragsize, db->cnt_factor, db->dma_fragsize); - dbg("numfrag=%d, dmasize=%d", db->numfrag, db->dmasize); -#endif - - db->ready = 1; - return 0; -} - -static inline int prog_dmabuf_adc(struct au1000_state *s) -{ - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc); - -} - -static inline int prog_dmabuf_dac(struct au1000_state *s) -{ - stop_dac(s); - return prog_dmabuf(s, &s->dma_dac); -} - - -/* hold spinlock for the following */ -static irqreturn_t dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct au1000_state *s = (struct au1000_state *) dev_id; - struct dmabuf *dac = &s->dma_dac; - unsigned long newptr; - u32 ac97c_stat, buff_done; - - ac97c_stat = au_readl(AC97C_STATUS); -#ifdef AU1000_VERBOSE_DEBUG - if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) - dbg("AC97C status = 0x%08x", ac97c_stat); -#endif - - if ((buff_done = get_dma_buffer_done(dac->dmanr)) == 0) { - /* fastpath out, to ease interrupt sharing */ - return IRQ_HANDLED; - } - - spin_lock(&s->lock); - - if (buff_done != (DMA_D0 | DMA_D1)) { - dac->nextOut += dac->dma_fragsize; - if (dac->nextOut >= dac->rawbuf + dac->dmasize) - dac->nextOut -= dac->dmasize; - - /* update playback pointers */ - newptr = virt_to_phys(dac->nextOut) + dac->dma_fragsize; - if (newptr >= dac->dmaaddr + dac->dmasize) - newptr -= dac->dmasize; - - dac->count -= dac->dma_fragsize; - dac->total_bytes += dac->dma_fragsize; - - if (dac->count <= 0) { -#ifdef AU1000_VERBOSE_DEBUG - dbg("dac underrun"); -#endif - spin_unlock(&s->lock); - stop_dac(s); - spin_lock(&s->lock); - dac->count = 0; - dac->nextIn = dac->nextOut; - } else if (buff_done == DMA_D0) { - clear_dma_done0(dac->dmanr); // clear DMA done bit - set_dma_count0(dac->dmanr, dac->dma_fragsize>>1); - set_dma_addr0(dac->dmanr, newptr); - enable_dma_buffer0(dac->dmanr); // reenable - } else { - clear_dma_done1(dac->dmanr); // clear DMA done bit - set_dma_count1(dac->dmanr, dac->dma_fragsize>>1); - set_dma_addr1(dac->dmanr, newptr); - enable_dma_buffer1(dac->dmanr); // reenable - } - } else { - // both done bits set, we missed an interrupt - spin_unlock(&s->lock); - stop_dac(s); - spin_lock(&s->lock); - - dac->nextOut += 2*dac->dma_fragsize; - if (dac->nextOut >= dac->rawbuf + dac->dmasize) - dac->nextOut -= dac->dmasize; - - dac->count -= 2*dac->dma_fragsize; - dac->total_bytes += 2*dac->dma_fragsize; - - if (dac->count > 0) { - spin_unlock(&s->lock); - start_dac(s); - spin_lock(&s->lock); - } - } - - /* wake up anybody listening */ - if (waitqueue_active(&dac->wait)) - wake_up(&dac->wait); - - spin_unlock(&s->lock); - - return IRQ_HANDLED; -} - - -static irqreturn_t adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct au1000_state *s = (struct au1000_state *) dev_id; - struct dmabuf *adc = &s->dma_adc; - unsigned long newptr; - u32 ac97c_stat, buff_done; - - ac97c_stat = au_readl(AC97C_STATUS); -#ifdef AU1000_VERBOSE_DEBUG - if (ac97c_stat & (AC97C_RU | AC97C_RO)) - dbg("AC97C status = 0x%08x", ac97c_stat); -#endif - - if ((buff_done = get_dma_buffer_done(adc->dmanr)) == 0) { - /* fastpath out, to ease interrupt sharing */ - return IRQ_HANDLED; - } - - spin_lock(&s->lock); - - if (buff_done != (DMA_D0 | DMA_D1)) { - if (adc->count + adc->dma_fragsize > adc->dmasize) { - // Overrun. Stop ADC and log the error - spin_unlock(&s->lock); - stop_adc(s); - adc->error++; - err("adc overrun"); - return IRQ_NONE; - } - - adc->nextIn += adc->dma_fragsize; - if (adc->nextIn >= adc->rawbuf + adc->dmasize) - adc->nextIn -= adc->dmasize; - - /* update capture pointers */ - newptr = virt_to_phys(adc->nextIn) + adc->dma_fragsize; - if (newptr >= adc->dmaaddr + adc->dmasize) - newptr -= adc->dmasize; - - adc->count += adc->dma_fragsize; - adc->total_bytes += adc->dma_fragsize; - - if (buff_done == DMA_D0) { - clear_dma_done0(adc->dmanr); // clear DMA done bit - set_dma_count0(adc->dmanr, adc->dma_fragsize>>1); - set_dma_addr0(adc->dmanr, newptr); - enable_dma_buffer0(adc->dmanr); // reenable - } else { - clear_dma_done1(adc->dmanr); // clear DMA done bit - set_dma_count1(adc->dmanr, adc->dma_fragsize>>1); - set_dma_addr1(adc->dmanr, newptr); - enable_dma_buffer1(adc->dmanr); // reenable - } - } else { - // both done bits set, we missed an interrupt - spin_unlock(&s->lock); - stop_adc(s); - spin_lock(&s->lock); - - if (adc->count + 2*adc->dma_fragsize > adc->dmasize) { - // Overrun. Log the error - adc->error++; - err("adc overrun"); - spin_unlock(&s->lock); - return IRQ_NONE; - } - - adc->nextIn += 2*adc->dma_fragsize; - if (adc->nextIn >= adc->rawbuf + adc->dmasize) - adc->nextIn -= adc->dmasize; - - adc->count += 2*adc->dma_fragsize; - adc->total_bytes += 2*adc->dma_fragsize; - - spin_unlock(&s->lock); - start_adc(s); - spin_lock(&s->lock); - } - - /* wake up anybody listening */ - if (waitqueue_active(&adc->wait)) - wake_up(&adc->wait); - - spin_unlock(&s->lock); - - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -static loff_t au1000_llseek(struct file *file, loff_t offset, int origin) -{ - return -ESPIPE; -} - - -static int au1000_open_mixdev(struct inode *inode, struct file *file) -{ - file->private_data = &au1000_state; - return nonseekable_open(inode, file); -} - -static int au1000_release_mixdev(struct inode *inode, struct file *file) -{ - return 0; -} - -static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, - unsigned long arg) -{ - return codec->mixer_ioctl(codec, cmd, arg); -} - -static int au1000_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - struct ac97_codec *codec = &s->codec; - - return mixdev_ioctl(codec, cmd, arg); -} - -static /*const */ struct file_operations au1000_mixer_fops = { - .owner = THIS_MODULE, - .llseek = au1000_llseek, - .ioctl = au1000_ioctl_mixdev, - .open = au1000_open_mixdev, - .release = au1000_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct au1000_state *s, int nonblock) -{ - unsigned long flags; - int count, tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) - return 0; - - for (;;) { - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) - return -EBUSY; - tmo = 1000 * count / (s->no_vra ? - 48000 : s->dma_dac.sample_rate); - tmo /= s->dma_dac.dma_bytes_per_sample; - au1000_delay(tmo); - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static inline u8 S16_TO_U8(s16 ch) -{ - return (u8) (ch >> 8) + 0x80; -} -static inline s16 U8_TO_S16(u8 ch) -{ - return (s16) (ch - 0x80) << 8; -} - -/* - * Translates user samples to dma buffer suitable for AC'97 DAC data: - * If mono, copy left channel to right channel in dma buffer. - * If 8 bit samples, cvt to 16-bit before writing to dma buffer. - * If interpolating (no VRA), duplicate every audio frame src_factor times. - */ -static int translate_from_user(struct dmabuf *db, - char* dmabuf, - char* userbuf, - int dmacount) -{ - int sample, i; - int interp_bytes_per_sample; - int num_samples; - int mono = (db->num_channels == 1); - char usersample[12]; - s16 ch, dmasample[6]; - - if (db->sample_size == 16 && !mono && db->src_factor == 1) { - // no translation necessary, just copy - if (copy_from_user(dmabuf, userbuf, dmacount)) - return -EFAULT; - return dmacount; - } - - interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; - num_samples = dmacount / interp_bytes_per_sample; - - for (sample = 0; sample < num_samples; sample++) { - if (copy_from_user(usersample, userbuf, - db->user_bytes_per_sample)) { - dbg("%s: fault", __FUNCTION__); - return -EFAULT; - } - - for (i = 0; i < db->num_channels; i++) { - if (db->sample_size == 8) - ch = U8_TO_S16(usersample[i]); - else - ch = *((s16 *) (&usersample[i * 2])); - dmasample[i] = ch; - if (mono) - dmasample[i + 1] = ch; // right channel - } - - // duplicate every audio frame src_factor times - for (i = 0; i < db->src_factor; i++) - memcpy(dmabuf, dmasample, db->dma_bytes_per_sample); - - userbuf += db->user_bytes_per_sample; - dmabuf += interp_bytes_per_sample; - } - - return num_samples * interp_bytes_per_sample; -} - -/* - * Translates AC'97 ADC samples to user buffer: - * If mono, send only left channel to user buffer. - * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer. - * If decimating (no VRA), skip over src_factor audio frames. - */ -static int translate_to_user(struct dmabuf *db, - char* userbuf, - char* dmabuf, - int dmacount) -{ - int sample, i; - int interp_bytes_per_sample; - int num_samples; - int mono = (db->num_channels == 1); - char usersample[12]; - - if (db->sample_size == 16 && !mono && db->src_factor == 1) { - // no translation necessary, just copy - if (copy_to_user(userbuf, dmabuf, dmacount)) - return -EFAULT; - return dmacount; - } - - interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; - num_samples = dmacount / interp_bytes_per_sample; - - for (sample = 0; sample < num_samples; sample++) { - for (i = 0; i < db->num_channels; i++) { - if (db->sample_size == 8) - usersample[i] = - S16_TO_U8(*((s16 *) (&dmabuf[i * 2]))); - else - *((s16 *) (&usersample[i * 2])) = - *((s16 *) (&dmabuf[i * 2])); - } - - if (copy_to_user(userbuf, usersample, - db->user_bytes_per_sample)) { - dbg("%s: fault", __FUNCTION__); - return -EFAULT; - } - - userbuf += db->user_bytes_per_sample; - dmabuf += interp_bytes_per_sample; - } - - return num_samples * interp_bytes_per_sample; -} - -/* - * Copy audio data to/from user buffer from/to dma buffer, taking care - * that we wrap when reading/writing the dma buffer. Returns actual byte - * count written to or read from the dma buffer. - */ -static int copy_dmabuf_user(struct dmabuf *db, char* userbuf, - int count, int to_user) -{ - char *bufptr = to_user ? db->nextOut : db->nextIn; - char *bufend = db->rawbuf + db->dmasize; - int cnt, ret; - - if (bufptr + count > bufend) { - int partial = (int) (bufend - bufptr); - if (to_user) { - if ((cnt = translate_to_user(db, userbuf, - bufptr, partial)) < 0) - return cnt; - ret = cnt; - if ((cnt = translate_to_user(db, userbuf + partial, - db->rawbuf, - count - partial)) < 0) - return cnt; - ret += cnt; - } else { - if ((cnt = translate_from_user(db, bufptr, userbuf, - partial)) < 0) - return cnt; - ret = cnt; - if ((cnt = translate_from_user(db, db->rawbuf, - userbuf + partial, - count - partial)) < 0) - return cnt; - ret += cnt; - } - } else { - if (to_user) - ret = translate_to_user(db, userbuf, bufptr, count); - else - ret = translate_from_user(db, bufptr, userbuf, count); - } - - return ret; -} - - -static ssize_t au1000_read(struct file *file, char *buffer, - size_t count, loff_t *ppos) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - struct dmabuf *db = &s->dma_adc; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - int cnt, usercnt, avail; - - if (db->mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - count *= db->cnt_factor; - - mutex_lock(&s->sem); - add_wait_queue(&db->wait, &wait); - - while (count > 0) { - // wait for samples in ADC dma buffer - do { - if (db->stopped) - start_adc(s); - spin_lock_irqsave(&s->lock, flags); - avail = db->count; - if (avail <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (avail <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto out; - } - mutex_unlock(&s->sem); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto out2; - } - mutex_lock(&s->sem); - } - } while (avail <= 0); - - // copy from nextOut to user - if ((cnt = copy_dmabuf_user(db, buffer, - count > avail ? - avail : count, 1)) < 0) { - if (!ret) - ret = -EFAULT; - goto out; - } - - spin_lock_irqsave(&s->lock, flags); - db->count -= cnt; - db->nextOut += cnt; - if (db->nextOut >= db->rawbuf + db->dmasize) - db->nextOut -= db->dmasize; - spin_unlock_irqrestore(&s->lock, flags); - - count -= cnt; - usercnt = cnt / db->cnt_factor; - buffer += usercnt; - ret += usercnt; - } // while (count > 0) - -out: - mutex_unlock(&s->sem); -out2: - remove_wait_queue(&db->wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t au1000_write(struct file *file, const char *buffer, - size_t count, loff_t * ppos) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - struct dmabuf *db = &s->dma_dac; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - int cnt, usercnt, avail; - -#ifdef AU1000_VERBOSE_DEBUG - dbg("write: count=%d", count); -#endif - - if (db->mapped) - return -ENXIO; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - - count *= db->cnt_factor; - - mutex_lock(&s->sem); - add_wait_queue(&db->wait, &wait); - - while (count > 0) { - // wait for space in playback buffer - do { - spin_lock_irqsave(&s->lock, flags); - avail = (int) db->dmasize - db->count; - if (avail <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (avail <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto out; - } - mutex_unlock(&s->sem); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto out2; - } - mutex_lock(&s->sem); - } - } while (avail <= 0); - - // copy from user to nextIn - if ((cnt = copy_dmabuf_user(db, (char *) buffer, - count > avail ? - avail : count, 0)) < 0) { - if (!ret) - ret = -EFAULT; - goto out; - } - - spin_lock_irqsave(&s->lock, flags); - db->count += cnt; - db->nextIn += cnt; - if (db->nextIn >= db->rawbuf + db->dmasize) - db->nextIn -= db->dmasize; - spin_unlock_irqrestore(&s->lock, flags); - if (db->stopped) - start_dac(s); - - count -= cnt; - usercnt = cnt / db->cnt_factor; - buffer += usercnt; - ret += usercnt; - } // while (count > 0) - -out: - mutex_unlock(&s->sem); -out2: - remove_wait_queue(&db->wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - - -/* No kernel lock - we have our own spinlock */ -static unsigned int au1000_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - - spin_lock_irqsave(&s->lock, flags); - - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= - (signed)s->dma_dac.dma_fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed) s->dma_dac.dmasize >= - s->dma_dac.count + (signed)s->dma_dac.dma_fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int au1000_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - struct dmabuf *db; - unsigned long size; - int ret = 0; - - dbg("%s", __FUNCTION__); - - lock_kernel(); - mutex_lock(&s->sem); - if (vma->vm_flags & VM_WRITE) - db = &s->dma_dac; - else if (vma->vm_flags & VM_READ) - db = &s->dma_adc; - else { - ret = -EINVAL; - goto out; - } - if (vma->vm_pgoff != 0) { - ret = -EINVAL; - goto out; - } - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) { - ret = -EINVAL; - goto out; - } - if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), - size, vma->vm_page_prot)) { - ret = -EAGAIN; - goto out; - } - vma->vm_flags &= ~VM_IO; - db->mapped = 1; -out: - mutex_unlock(&s->sem); - unlock_kernel(); - return ret; -} - - -#ifdef AU1000_VERBOSE_DEBUG -static struct ioctl_str_t { - unsigned int cmd; - const char *str; -} ioctl_str[] = { - {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, - {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, - {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, - {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, - {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, - {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, - {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, - {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, - {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, - {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, - {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, - {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, - {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, - {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, - {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, - {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, - {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, - {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, - {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, - {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, - {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, - {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, - {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, - {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, - {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, - {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, - {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, - {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, - {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, - {OSS_GETVERSION, "OSS_GETVERSION"}, - {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, - {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, - {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, - {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} -}; -#endif - -// Need to hold a spin-lock before calling this! -static int dma_count_done(struct dmabuf *db) -{ - if (db->stopped) - return 0; - - return db->dma_fragsize - get_dma_residue(db->dmanr); -} - - -static int au1000_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret, diff; - - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - -#ifdef AU1000_VERBOSE_DEBUG - for (count=0; countf_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | - DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(); - s->dma_dac.count = s->dma_dac.total_bytes = 0; - s->dma_dac.nextIn = s->dma_dac.nextOut = - s->dma_dac.rawbuf; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(); - s->dma_adc.count = s->dma_adc.total_bytes = 0; - s->dma_adc.nextIn = s->dma_adc.nextOut = - s->dma_adc.rawbuf; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - set_dac_rate(s, val); - } - if (s->open_mode & FMODE_READ) - if ((ret = prog_dmabuf_adc(s))) - return ret; - if (s->open_mode & FMODE_WRITE) - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return put_user((file->f_mode & FMODE_READ) ? - s->dma_adc.sample_rate : - s->dma_dac.sample_rate, - (int *)arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *) arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.num_channels = val ? 2 : 1; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.num_channels = val ? 2 : 1; - if (s->codec_ext_caps & AC97_EXT_DACS) { - // disable surround and center/lfe in AC'97 - u16 ext_stat = rdcodec(&s->codec, - AC97_EXTENDED_STATUS); - wrcodec(&s->codec, AC97_EXTENDED_STATUS, - ext_stat | (AC97_EXTSTAT_PRI | - AC97_EXTSTAT_PRJ | - AC97_EXTSTAT_PRK)); - } - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != 0) { - if (file->f_mode & FMODE_READ) { - if (val < 0 || val > 2) - return -EINVAL; - stop_adc(s); - s->dma_adc.num_channels = val; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - switch (val) { - case 1: - case 2: - break; - case 3: - case 5: - return -EINVAL; - case 4: - if (!(s->codec_ext_caps & - AC97_EXTID_SDAC)) - return -EINVAL; - break; - case 6: - if ((s->codec_ext_caps & - AC97_EXT_DACS) != AC97_EXT_DACS) - return -EINVAL; - break; - default: - return -EINVAL; - } - - stop_dac(s); - if (val <= 2 && - (s->codec_ext_caps & AC97_EXT_DACS)) { - // disable surround and center/lfe - // channels in AC'97 - u16 ext_stat = - rdcodec(&s->codec, - AC97_EXTENDED_STATUS); - wrcodec(&s->codec, - AC97_EXTENDED_STATUS, - ext_stat | (AC97_EXTSTAT_PRI | - AC97_EXTSTAT_PRJ | - AC97_EXTSTAT_PRK)); - } else if (val >= 4) { - // enable surround, center/lfe - // channels in AC'97 - u16 ext_stat = - rdcodec(&s->codec, - AC97_EXTENDED_STATUS); - ext_stat &= ~AC97_EXTSTAT_PRJ; - if (val == 6) - ext_stat &= - ~(AC97_EXTSTAT_PRI | - AC97_EXTSTAT_PRK); - wrcodec(&s->codec, - AC97_EXTENDED_STATUS, - ext_stat); - } - - s->dma_dac.num_channels = val; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } - return put_user(val, (int *) arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if (val == AFMT_S16_LE) - s->dma_adc.sample_size = 16; - else { - val = AFMT_U8; - s->dma_adc.sample_size = 8; - } - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if (val == AFMT_S16_LE) - s->dma_dac.sample_size = 16; - else { - val = AFMT_U8; - s->dma_dac.sample_size = 8; - } - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - } else { - if (file->f_mode & FMODE_READ) - val = (s->dma_adc.sample_size == 16) ? - AFMT_S16_LE : AFMT_U8; - else - val = (s->dma_dac.sample_size == 16) ? - AFMT_S16_LE : AFMT_U8; - } - return put_user(val, (int *) arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) - val |= PCM_ENABLE_OUTPUT; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, (int *) arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *) arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) - start_adc(s); - else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) - start_dac(s); - else - stop_dac(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - abinfo.fragsize = s->dma_dac.fragsize; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - count -= dma_count_done(&s->dma_dac); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = (s->dma_dac.dmasize - count) / - s->dma_dac.cnt_factor; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; -#ifdef AU1000_VERBOSE_DEBUG - dbg("bytes=%d, fragments=%d", abinfo.bytes, abinfo.fragments); -#endif - return copy_to_user((void *) arg, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - abinfo.fragsize = s->dma_adc.fragsize; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_adc.count; - count += dma_count_done(&s->dma_adc); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - abinfo.bytes = count / s->dma_adc.cnt_factor; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - return copy_to_user((void *) arg, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - count -= dma_count_done(&s->dma_dac); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - count /= s->dma_dac.cnt_factor; - return put_user(count, (int *) arg); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (!s->dma_adc.stopped) { - diff = dma_count_done(&s->dma_adc); - count += diff; - cinfo.bytes += diff; - cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff - - s->dma_adc.dmaaddr; - } else - cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) - - s->dma_adc.dmaaddr; - if (s->dma_adc.mapped) - s->dma_adc.count &= (s->dma_adc.dma_fragsize-1); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (!s->dma_dac.stopped) { - diff = dma_count_done(&s->dma_dac); - count -= diff; - cinfo.bytes += diff; - cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff - - s->dma_dac.dmaaddr; - } else - cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) - - s->dma_dac.dmaaddr; - if (s->dma_dac.mapped) - s->dma_dac.count &= (s->dma_dac.dma_fragsize-1); - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac.fragshift; - return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) - return put_user(s->dma_dac.fragsize, (int *) arg); - else - return put_user(s->dma_adc.fragsize, (int *) arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *) arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.subdivision = val; - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.subdivision = val; - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? - s->dma_adc.sample_rate : - s->dma_dac.sample_rate, - (int *)arg); - - case SOUND_PCM_READ_CHANNELS: - if (file->f_mode & FMODE_READ) - return put_user(s->dma_adc.num_channels, (int *)arg); - else - return put_user(s->dma_dac.num_channels, (int *)arg); - - case SOUND_PCM_READ_BITS: - if (file->f_mode & FMODE_READ) - return put_user(s->dma_adc.sample_size, (int *)arg); - else - return put_user(s->dma_dac.sample_size, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } - - return mixdev_ioctl(&s->codec, cmd, arg); -} - - -static int au1000_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - struct au1000_state *s = &au1000_state; - int ret; - -#ifdef AU1000_VERBOSE_DEBUG - if (file->f_flags & O_NONBLOCK) - dbg("%s: non-blocking", __FUNCTION__); - else - dbg("%s: blocking", __FUNCTION__); -#endif - - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - - stop_dac(s); - stop_adc(s); - - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = - s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; - s->dma_adc.num_channels = 1; - s->dma_adc.sample_size = 8; - set_adc_rate(s, 8000); - if ((minor & 0xf) == SND_DEV_DSP16) - s->dma_adc.sample_size = 16; - } - - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = - s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; - s->dma_dac.num_channels = 1; - s->dma_dac.sample_size = 8; - set_dac_rate(s, 8000); - if ((minor & 0xf) == SND_DEV_DSP16) - s->dma_dac.sample_size = 16; - } - - if (file->f_mode & FMODE_READ) { - if ((ret = prog_dmabuf_adc(s))) - return ret; - } - if (file->f_mode & FMODE_WRITE) { - if ((ret = prog_dmabuf_dac(s))) - return ret; - } - - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - mutex_unlock(&s->open_mutex); - mutex_init(&s->sem); - return nonseekable_open(inode, file); -} - -static int au1000_release(struct inode *inode, struct file *file) -{ - struct au1000_state *s = (struct au1000_state *)file->private_data; - - lock_kernel(); - - if (file->f_mode & FMODE_WRITE) { - unlock_kernel(); - drain_dac(s, file->f_flags & O_NONBLOCK); - lock_kernel(); - } - - mutex_lock(&s->open_mutex); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - dealloc_dmabuf(s, &s->dma_dac); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); - mutex_unlock(&s->open_mutex); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static /*const */ struct file_operations au1000_audio_fops = { - .owner = THIS_MODULE, - .llseek = au1000_llseek, - .read = au1000_read, - .write = au1000_write, - .poll = au1000_poll, - .ioctl = au1000_ioctl, - .mmap = au1000_mmap, - .open = au1000_open, - .release = au1000_release, -}; - - -/* --------------------------------------------------------------------- */ - - -/* --------------------------------------------------------------------- */ - -/* - * for debugging purposes, we'll create a proc device that dumps the - * CODEC chipstate - */ - -#ifdef AU1000_DEBUG -static int proc_au1000_dump(char *buf, char **start, off_t fpos, - int length, int *eof, void *data) -{ - struct au1000_state *s = &au1000_state; - int cnt, len = 0; - - /* print out header */ - len += sprintf(buf + len, "\n\t\tAU1000 Audio Debug\n\n"); - - // print out digital controller state - len += sprintf(buf + len, "AU1000 Audio Controller registers\n"); - len += sprintf(buf + len, "---------------------------------\n"); - len += sprintf (buf + len, "AC97C_CONFIG = %08x\n", - au_readl(AC97C_CONFIG)); - len += sprintf (buf + len, "AC97C_STATUS = %08x\n", - au_readl(AC97C_STATUS)); - len += sprintf (buf + len, "AC97C_CNTRL = %08x\n", - au_readl(AC97C_CNTRL)); - - /* print out CODEC state */ - len += sprintf(buf + len, "\nAC97 CODEC registers\n"); - len += sprintf(buf + len, "----------------------\n"); - for (cnt = 0; cnt <= 0x7e; cnt += 2) - len += sprintf(buf + len, "reg %02x = %04x\n", - cnt, rdcodec(&s->codec, cnt)); - - if (fpos >= len) { - *start = buf; - *eof = 1; - return 0; - } - *start = buf + fpos; - if ((len -= fpos) > length) - return length; - *eof = 1; - return len; - -} -#endif /* AU1000_DEBUG */ - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); -MODULE_DESCRIPTION("Au1000 Audio Driver"); - -/* --------------------------------------------------------------------- */ - -static int __devinit au1000_probe(void) -{ - struct au1000_state *s = &au1000_state; - int val; -#ifdef AU1000_DEBUG - char proc_str[80]; -#endif - - memset(s, 0, sizeof(struct au1000_state)); - - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - mutex_init(&s->open_mutex); - spin_lock_init(&s->lock); - s->codec.private_data = s; - s->codec.id = 0; - s->codec.codec_read = rdcodec; - s->codec.codec_write = wrcodec; - s->codec.codec_wait = waitcodec; - - if (!request_mem_region(CPHYSADDR(AC97C_CONFIG), - 0x14, AU1000_MODULE_NAME)) { - err("AC'97 ports in use"); - return -1; - } - // Allocate the DMA Channels - if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX, - "audio DAC", - dac_dma_interrupt, - IRQF_DISABLED, s)) < 0) { - err("Can't get DAC DMA"); - goto err_dma1; - } - if ((s->dma_adc.dmanr = request_au1000_dma(DMA_ID_AC97C_RX, - "audio ADC", - adc_dma_interrupt, - IRQF_DISABLED, s)) < 0) { - err("Can't get ADC DMA"); - goto err_dma2; - } - - info("DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d", - s->dma_dac.dmanr, get_dma_done_irq(s->dma_dac.dmanr), - s->dma_adc.dmanr, get_dma_done_irq(s->dma_adc.dmanr)); - - // enable DMA coherency in read/write DMA channels - set_dma_mode(s->dma_dac.dmanr, - get_dma_mode(s->dma_dac.dmanr) & ~DMA_NC); - set_dma_mode(s->dma_adc.dmanr, - get_dma_mode(s->dma_adc.dmanr) & ~DMA_NC); - - /* register devices */ - - if ((s->dev_audio = register_sound_dsp(&au1000_audio_fops, -1)) < 0) - goto err_dev1; - if ((s->codec.dev_mixer = - register_sound_mixer(&au1000_mixer_fops, -1)) < 0) - goto err_dev2; - -#ifdef AU1000_DEBUG - /* intialize the debug proc device */ - s->ps = create_proc_read_entry(AU1000_MODULE_NAME, 0, NULL, - proc_au1000_dump, NULL); -#endif /* AU1000_DEBUG */ - - // configure pins for AC'97 - au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); - - // Assert reset for 10msec to the AC'97 controller, and enable clock - au_writel(AC97C_RS | AC97C_CE, AC97C_CNTRL); - au1000_delay(10); - au_writel(AC97C_CE, AC97C_CNTRL); - au1000_delay(10); // wait for clock to stabilize - - /* cold reset the AC'97 */ - au_writel(AC97C_RESET, AC97C_CONFIG); - au1000_delay(10); - au_writel(0, AC97C_CONFIG); - /* need to delay around 500msec(bleech) to give - some CODECs enough time to wakeup */ - au1000_delay(500); - - /* warm reset the AC'97 to start the bitclk */ - au_writel(AC97C_SG | AC97C_SYNC, AC97C_CONFIG); - udelay(100); - au_writel(0, AC97C_CONFIG); - - /* codec init */ - if (!ac97_probe_codec(&s->codec)) - goto err_dev3; - - s->codec_base_caps = rdcodec(&s->codec, AC97_RESET); - s->codec_ext_caps = rdcodec(&s->codec, AC97_EXTENDED_ID); - info("AC'97 Base/Extended ID = %04x/%04x", - s->codec_base_caps, s->codec_ext_caps); - - /* - * On the Pb1000, audio playback is on the AUX_OUT - * channel (which defaults to LNLVL_OUT in AC'97 - * rev 2.2) so make sure this channel is listed - * as supported (soundcard.h calls this channel - * ALTPCM). ac97_codec.c does not handle detection - * of this channel correctly. - */ - s->codec.supported_mixers |= SOUND_MASK_ALTPCM; - /* - * Now set AUX_OUT's default volume. - */ - val = 0x4343; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_ALTPCM, - (unsigned long) &val); - - if (!(s->codec_ext_caps & AC97_EXTID_VRA)) { - // codec does not support VRA - s->no_vra = 1; - } else if (!vra) { - // Boot option says disable VRA - u16 ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS); - wrcodec(&s->codec, AC97_EXTENDED_STATUS, - ac97_extstat & ~AC97_EXTSTAT_VRA); - s->no_vra = 1; - } - if (s->no_vra) - info("no VRA, interpolating and decimating"); - - /* set mic to be the recording source */ - val = SOUND_MASK_MIC; - mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, - (unsigned long) &val); - -#ifdef AU1000_DEBUG - sprintf(proc_str, "driver/%s/%d/ac97", AU1000_MODULE_NAME, - s->codec.id); - s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, - ac97_read_proc, &s->codec); -#endif - -#ifdef CONFIG_MIPS_XXS1500 - /* deassert eapd */ - wrcodec(&s->codec, AC97_POWER_CONTROL, - rdcodec(&s->codec, AC97_POWER_CONTROL) & ~0x8000); - /* mute a number of signals which seem to be causing problems - * if not muted. - */ - wrcodec(&s->codec, AC97_PCBEEP_VOL, 0x8000); - wrcodec(&s->codec, AC97_PHONE_VOL, 0x8008); - wrcodec(&s->codec, AC97_MIC_VOL, 0x8008); - wrcodec(&s->codec, AC97_LINEIN_VOL, 0x8808); - wrcodec(&s->codec, AC97_CD_VOL, 0x8808); - wrcodec(&s->codec, AC97_VIDEO_VOL, 0x8808); - wrcodec(&s->codec, AC97_AUX_VOL, 0x8808); - wrcodec(&s->codec, AC97_PCMOUT_VOL, 0x0808); - wrcodec(&s->codec, AC97_GENERAL_PURPOSE, 0x2000); -#endif - - return 0; - - err_dev3: - unregister_sound_mixer(s->codec.dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - free_au1000_dma(s->dma_adc.dmanr); - err_dma2: - free_au1000_dma(s->dma_dac.dmanr); - err_dma1: - release_mem_region(CPHYSADDR(AC97C_CONFIG), 0x14); - return -1; -} - -static void au1000_remove(void) -{ - struct au1000_state *s = &au1000_state; - - if (!s) - return; -#ifdef AU1000_DEBUG - if (s->ps) - remove_proc_entry(AU1000_MODULE_NAME, NULL); -#endif /* AU1000_DEBUG */ - synchronize_irq(); - free_au1000_dma(s->dma_adc.dmanr); - free_au1000_dma(s->dma_dac.dmanr); - release_mem_region(CPHYSADDR(AC97C_CONFIG), 0x14); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->codec.dev_mixer); -} - -static int __init init_au1000(void) -{ - info("stevel@mvista.com, built " __TIME__ " on " __DATE__); - return au1000_probe(); -} - -static void __exit cleanup_au1000(void) -{ - info("unloading"); - au1000_remove(); -} - -module_init(init_au1000); -module_exit(cleanup_au1000); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -static int __init au1000_setup(char *options) -{ - char *this_opt; - - if (!options || !*options) - return 0; - - while ((this_opt = strsep(&options, ","))) { - if (!*this_opt) - continue; - if (!strncmp(this_opt, "vra", 3)) { - vra = 1; - } - } - - return 1; -} - -__setup("au1000_audio=", au1000_setup); - -#endif /* MODULE */ diff --git a/sound/oss/audio_syms.c b/sound/oss/audio_syms.c deleted file mode 100644 index 5da217fcbe..0000000000 --- a/sound/oss/audio_syms.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Exported symbols for audio driver. - */ - -#include - -char audio_syms_symbol; - -#include "sound_config.h" -#include "sound_calls.h" - -EXPORT_SYMBOL(DMAbuf_start_dma); -EXPORT_SYMBOL(DMAbuf_open_dma); -EXPORT_SYMBOL(DMAbuf_close_dma); -EXPORT_SYMBOL(DMAbuf_inputintr); -EXPORT_SYMBOL(DMAbuf_outputintr); diff --git a/sound/oss/awe_hw.h b/sound/oss/awe_hw.h deleted file mode 100644 index ab00c3c67e..0000000000 --- a/sound/oss/awe_hw.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * sound/oss/awe_hw.h - * - * Access routines and definitions for the low level driver for the - * Creative AWE32/SB32/AWE64 wave table synth. - * version 0.4.4; Jan. 4, 2000 - * - * Copyright (C) 1996-2000 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 diff --git a/sound/oss/awe_wave.c b/sound/oss/awe_wave.c deleted file mode 100644 index 01c592cee0..0000000000 --- a/sound/oss/awe_wave.c +++ /dev/null @@ -1,6149 +0,0 @@ -/* - * sound/oss/awe_wave.c - * - * The low level driver for the AWE32/SB32/AWE64 wave table synth. - * version 0.4.4; Jan. 4, 2000 - * - * Copyright (C) 1996-2000 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. - */ - -/* - * Changelog: - * Aug 18, 2003, Adam Belay - * - detection code rewrite - */ - -#include -#include -#include -#include -#include -#include - -#include "sound_config.h" - -#include "awe_wave.h" -#include "awe_hw.h" - -#ifdef AWE_HAS_GUS_COMPATIBILITY -#include "tuning.h" -#include -#endif - -/* - * 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 - */ - -typedef struct _sf_list sf_list; -typedef struct _awe_voice_list awe_voice_list; -typedef struct _awe_sample_list awe_sample_list; - -/* soundfont record */ -struct _sf_list { - unsigned short sf_id; /* id number */ - unsigned short type; /* lock & shared flags */ - int num_info; /* current info table index */ - int num_sample; /* current sample table index */ - int mem_ptr; /* current word byte pointer */ - awe_voice_list *infos, *last_infos; /* instruments */ - awe_sample_list *samples, *last_samples; /* samples */ -#ifdef AWE_ALLOW_SAMPLE_SHARING - sf_list *shared; /* shared list */ - unsigned char name[AWE_PATCH_NAME_LEN]; /* sharing id */ -#endif - sf_list *next, *prev; -}; - -/* instrument list */ -struct _awe_voice_list { - awe_voice_info v; /* instrument information */ - sf_list *holder; /* parent sf_list of this record */ - unsigned char bank, instr; /* preset number information */ - char type, disabled; /* type=normal/mapped, disabled=boolean */ - awe_voice_list *next; /* linked list with same sf_id */ - awe_voice_list *next_instr; /* instrument list */ - awe_voice_list *next_bank; /* hash table list */ -}; - -/* voice list type */ -#define V_ST_NORMAL 0 -#define V_ST_MAPPED 1 - -/* sample list */ -struct _awe_sample_list { - awe_sample_info v; /* sample information */ - sf_list *holder; /* parent sf_list of this record */ - awe_sample_list *next; /* linked list with same sf_id */ -}; - -/* sample and information table */ -static int current_sf_id; /* current number of fonts */ -static int locked_sf_id; /* locked position */ -static sf_list *sfhead, *sftail; /* linked-lists */ - -#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0) -#define awe_free_info() (sftail ? sftail->num_info : 0) -#define awe_free_sample() (sftail ? sftail->num_sample : 0) - -#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 awe_voice_list *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 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 - -static int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */ -static int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */ -#ifdef CONFIG_PNP -static int isapnp = -1; -#else -static int isapnp; -#endif - -MODULE_AUTHOR("Takashi Iwai "); -MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver"); -MODULE_LICENSE("GPL"); - -module_param(io, int, 0); -MODULE_PARM_DESC(io, "base i/o port of Emu8000"); -module_param(memsize, int, 0); -MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes"); -module_param(isapnp, bool, 0); -MODULE_PARM_DESC(isapnp, "use ISAPnP detection"); - -/* 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; /* sample already loaded? */ - -static char atten_relative = FALSE; -static short atten_offset; - -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; /* 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) */ - 400 /* instr_bank_size */ -}; - - -static struct voice_alloc_info *voice_alloc; /* set at initialization */ - - -/* - * function prototypes - */ - -static int 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 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, void __user * 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 __user *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_key(int bank, int preset, int note); -static awe_voice_list *awe_search_instr(int bank, int preset, int note); -static int awe_search_multi_voices(awe_voice_list *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 __user *addr, int count); -static int awe_close_patch(awe_patch_info *patch, const char __user *addr, int count); -static int awe_unload_patch(awe_patch_info *patch, const char __user *addr, int count); -static int awe_load_info(awe_patch_info *patch, const char __user *addr, int count); -static int awe_remove_info(awe_patch_info *patch, const char __user *addr, int count); -static int awe_load_data(awe_patch_info *patch, const char __user *addr, int count); -static int awe_replace_data(awe_patch_info *patch, const char __user *addr, int count); -static int awe_load_map(awe_patch_info *patch, const char __user *addr, int count); -#ifdef AWE_HAS_GUS_COMPATIBILITY -static int awe_load_guspatch(const char __user *addr, int offs, int size, int pmgr_flag); -#endif -/*static int awe_probe_info(awe_patch_info *patch, const char __user *addr, int count);*/ -static int awe_probe_data(awe_patch_info *patch, const char __user *addr, int count); -static sf_list *check_patch_opened(int type, char *name); -static int awe_write_wave_data(const char __user *addr, int offset, awe_sample_list *sp, int channels); -static int awe_create_sf(int type, char *name); -static void awe_free_sf(sf_list *sf); -static void add_sf_info(sf_list *sf, awe_voice_list *rec); -static void add_sf_sample(sf_list *sf, awe_sample_list *smp); -static void purge_old_list(awe_voice_list *rec, awe_voice_list *next); -static void add_info_list(awe_voice_list *rec); -static void awe_remove_samples(int sf_id); -static void rebuild_preset_list(void); -static short awe_set_sample(awe_voice_list *rec); -static awe_sample_list *search_sample_index(sf_list *sf, int sample); - -static int is_identical_holder(sf_list *sf1, sf_list *sf2); -#ifdef AWE_ALLOW_SAMPLE_SHARING -static int is_identical_name(unsigned char *name, sf_list *p); -static int is_shared_sf(unsigned char *name); -static int info_duplicated(sf_list *sf, 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 __user *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 __user *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 = -{ - .owner = THIS_MODULE, - .id = "EMU8K", - .info = &awe_info, - .midi_dev = 0, - .synth_type = SYNTH_TYPE_SAMPLE, - .synth_subtype = SAMPLE_TYPE_AWE32, - .open = awe_open, - .close = awe_close, - .ioctl = awe_ioctl, - .kill_note = awe_kill_note, - .start_note = awe_start_note, - .set_instr = awe_set_instr_2, - .reset = awe_reset, - .hw_control = awe_hw_control, - .load_patch = awe_load_patch, - .aftertouch = awe_aftertouch, - .controller = awe_controller, - .panning = awe_panning, - .volume_method = awe_volume_method, - .bender = awe_bender, - .alloc_voice = awe_alloc, - .setup_voice = awe_setup_voice -}; - -static void free_tables(void) -{ - if (sftail) { - sf_list *p, *prev; - for (p = sftail; p; p = prev) { - prev = p->prev; - awe_free_sf(p); - } - } - sfhead = sftail = NULL; -} - -/* - * clear sample tables - */ - -static void -awe_reset_samples(void) -{ - /* free all bank tables */ - memset(preset_table, 0, sizeof(preset_table)); - 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; } - -/* write 16bit data */ -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 */ -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 */ -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 */ -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); -} -/* -static void awe_wait(unsigned short delay) -{ - udelay(((unsigned long)delay * 1000000L + 44099) / 44100); -} -*/ -#endif /* wait by loop */ - -/* write a word data */ -#define awe_write_dram(c) awe_poke(AWE_SMLD, c) - -/* - * AWE32 voice parameters - */ - -/* initialize voice_info record */ -static void -awe_init_voice_info(awe_voice_info *vp) -{ - 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); - - /* 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); - /* turn on envelope */ - awe_poke(AWE_DCYSUSV(voice), - FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, - vp->parm.voldcysus)); - - 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; - if (temp == 0) - voices[voice].aaux = 0xff; - else - voices[voice].aaux = (-temp) & 0xff; - 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)); - } -} - -/* 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((awe_voice_list*)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((awe_voice_list*)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((awe_voice_list*)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->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, void __user *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; - if (copy_to_user(arg, &awe_info, sizeof(awe_info))) - return -EFAULT; - return 0; - break; - - case SNDCTL_SEQ_RESETSAMPLES: - awe_reset(dev); - awe_reset_samples(); - return 0; - break; - - case SNDCTL_SEQ_PERCMODE: - /* what's this? */ - return 0; - break; - - case SNDCTL_SYNTH_MEMAVL: - return memsize - awe_free_mem_ptr() * 2; - break; - - default: - printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd); - return -EINVAL; - break; - } -} - - -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; -} - - -/* calculate hash key */ -static int -awe_search_key(int bank, int preset, int note) -{ - unsigned int key; - -#if 1 /* new hash table */ - if (bank == AWE_DRUM_BANK) - key = preset + note + 128; - else - key = bank + preset; -#else - key = preset; -#endif - key %= AWE_MAX_PRESETS; - - return (int)key; -} - - -/* search instrument from hash table */ -static awe_voice_list * -awe_search_instr(int bank, int preset, int note) -{ - awe_voice_list *p; - int key, key2; - - key = awe_search_key(bank, preset, note); - for (p = preset_table[key]; p; p = p->next_bank) { - if (p->instr == preset && p->bank == bank) - return p; - } - key2 = awe_search_key(bank, preset, 0); /* search default */ - if (key == key2) - return NULL; - for (p = preset_table[key2]; p; p = p->next_bank) { - if (p->instr == preset && p->bank == bank) - return p; - } - return NULL; -} - - -/* 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; - - if (! voice_in_range(voice)) - return -EINVAL; - - if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) - return -EINVAL; - - cinfo = &channels[voice]; - cinfo->instr = instr_no; - DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no)); - - 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(KERN_DEBUG "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")); - awe_reset(0); - 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; - if (value < 0 || value > 255) - 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 __user *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(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format); - return -EINVAL; - } - - if (count < AWE_PATCH_INFO_SIZE) { - printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); - return -EINVAL; - } - if (copy_from_user(((char*)&patch) + offs, addr + offs, - AWE_PATCH_INFO_SIZE - offs)) - return -EFAULT; - - count -= AWE_PATCH_INFO_SIZE; - if (count < patch.len) { - printk(KERN_WARNING "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_REMOVE_INFO: - rc = awe_remove_info(&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(KERN_WARNING "AWE32 Error: unknown patch format type %d\n", - patch.type); - rc = -EINVAL; - } - - return rc; -} - - -/* create an sf list record */ -static int -awe_create_sf(int type, char *name) -{ - sf_list *rec; - - /* terminate sounds */ - awe_reset(0); - rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL); - if (rec == NULL) - return 1; /* no memory */ - 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 = rec->last_infos = NULL; - rec->samples = rec->last_samples = NULL; - - /* add to linked-list */ - rec->next = NULL; - rec->prev = sftail; - if (sftail) - sftail->next = rec; - else - sfhead = rec; - sftail = rec; - current_sf_id++; - -#ifdef AWE_ALLOW_SAMPLE_SHARING - rec->shared = NULL; - if (name) - memcpy(rec->name, name, AWE_PATCH_NAME_LEN); - else - strcpy(rec->name, "*TEMPORARY*"); - if (current_sf_id > 1 && 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 */ - sf_list *p; - for (p = rec->prev; p; p = p->prev) { - if (is_identical_name(rec->name, p)) { - rec->shared = p; - break; - } - } - } - } -#endif /* allow sharing */ - - 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[4] = { - ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'), - AWE_MAJOR_VERSION, - }; - if (memcmp(name, id_head, 4) == 0) - return TRUE; - return FALSE; -} - -/* check if the given name matches to the existing list */ -static int is_identical_name(unsigned char *name, sf_list *p) -{ - char *id = p->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(sf_list *sf, awe_voice_list *rec) -{ - /* search for all sharing lists */ - for (; sf; sf = sf->shared) { - awe_voice_list *p; - for (p = sf->infos; p; p = p->next) { - 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 */ - - -/* free sf_list record */ -/* linked-list in this function is not cared */ -static void -awe_free_sf(sf_list *sf) -{ - if (sf->infos) { - awe_voice_list *p, *next; - for (p = sf->infos; p; p = next) { - next = p->next; - kfree(p); - } - } - if (sf->samples) { - awe_sample_list *p, *next; - for (p = sf->samples; p; p = next) { - next = p->next; - kfree(p); - } - } - kfree(sf); -} - - -/* open patch; create sf list and set opened flag */ -static int -awe_open_patch(awe_patch_info *patch, const char __user *addr, int count) -{ - awe_open_parm parm; - int shared; - - if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm))) - return -EFAULT; - shared = FALSE; - -#ifdef AWE_ALLOW_SAMPLE_SHARING - if (sftail && (parm.type & AWE_PAT_SHARED) != 0) { - /* is the previous font the same font? */ - if (is_identical_name(parm.name, sftail)) { - /* 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(KERN_ERR "AWE32: can't open: failed to alloc new list\n"); - return -ENOMEM; - } - } - patch_opened = TRUE; - return current_sf_id; -} - -/* check if the patch is already opened */ -static sf_list * -check_patch_opened(int type, char *name) -{ - if (! patch_opened) { - if (awe_create_sf(type, name)) { - printk(KERN_ERR "AWE32: failed to alloc new list\n"); - return NULL; - } - patch_opened = TRUE; - return sftail; - } - return sftail; -} - -/* close the patch; if no voice is loaded, remove the patch */ -static int -awe_close_patch(awe_patch_info *patch, const char __user *addr, int count) -{ - if (patch_opened && sftail) { - /* if no voice is loaded, release the current patch */ - if (sftail->infos == NULL) { - awe_reset(0); - 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 __user *addr, int count) -{ - if (current_sf_id > 0 && current_sf_id > locked_sf_id) { - awe_reset(0); - awe_remove_samples(current_sf_id - 1); - } - return 0; -} - -/* allocate voice info list records */ -static awe_voice_list * -alloc_new_info(void) -{ - awe_voice_list *newlist; - - newlist = kmalloc(sizeof(*newlist), GFP_KERNEL); - if (newlist == NULL) { - printk(KERN_ERR "AWE32: can't alloc info table\n"); - return NULL; - } - return newlist; -} - -/* allocate sample info list records */ -static awe_sample_list * -alloc_new_sample(void) -{ - awe_sample_list *newlist; - - newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); - if (newlist == NULL) { - printk(KERN_ERR "AWE32: can't alloc sample table\n"); - return NULL; - } - return newlist; -} - -/* load voice map */ -static int -awe_load_map(awe_patch_info *patch, const char __user *addr, int count) -{ - awe_voice_map map; - awe_voice_list *rec, *p; - sf_list *sf; - - /* get the link info */ - if (count < sizeof(map)) { - printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); - return -EINVAL; - } - if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) - return -EFAULT; - - /* check if the identical mapping already exists */ - p = awe_search_instr(map.map_bank, map.map_instr, map.map_key); - for (; p; p = p->next_instr) { - if (p->type == V_ST_MAPPED && - p->v.start == map.src_instr && - p->v.end == map.src_bank && - p->v.fixkey == map.src_key) - return 0; /* already present! */ - } - - if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL) - return -ENOMEM; - - if ((rec = alloc_new_info()) == NULL) - return -ENOMEM; - - 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; - add_sf_info(sf, rec); - add_info_list(rec); - - 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 __user *addr, int count) -{ -#ifdef AWE_ALLOW_SAMPLE_SHARING - awe_voice_map map; - awe_voice_list *p; - - if (! patch_opened) - return -EINVAL; - - /* get the link info */ - if (count < sizeof(map)) { - printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); - return -EINVAL; - } - if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) - return -EFAULT; - - /* check if the identical mapping already exists */ - if (sftail == NULL) - return -EINVAL; - p = awe_search_instr(map.src_bank, map.src_instr, map.src_key); - for (; p; p = p->next_instr) { - if (p->type == V_ST_NORMAL && - is_identical_holder(p->holder, sftail) && - p->v.low <= map.src_key && - 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 __user *addr, int count) -{ -#ifdef AWE_ALLOW_SAMPLE_SHARING - if (! patch_opened) - return -EINVAL; - - /* search the specified sample by optarg */ - if (search_sample_index(sftail, patch->optarg) != NULL) - return 0; -#endif /* allow sharing */ - return -EINVAL; -} - - -/* remove the present instrument layers */ -static int -remove_info(sf_list *sf, int bank, int instr) -{ - awe_voice_list *prev, *next, *p; - int removed = 0; - - prev = NULL; - for (p = sf->infos; p; p = next) { - next = p->next; - if (p->type == V_ST_NORMAL && - p->bank == bank && p->instr == instr) { - /* remove this layer */ - if (prev) - prev->next = next; - else - sf->infos = next; - if (p == sf->last_infos) - sf->last_infos = prev; - sf->num_info--; - removed++; - kfree(p); - } else - prev = p; - } - if (removed) - rebuild_preset_list(); - return removed; -} - -/* load voice information data */ -static int -awe_load_info(awe_patch_info *patch, const char __user *addr, int count) -{ - int offset; - awe_voice_rec_hdr hdr; - int i; - int total_size; - sf_list *sf; - awe_voice_list *rec; - - if (count < AWE_VOICE_REC_SIZE) { - printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); - return -EINVAL; - } - - offset = AWE_PATCH_INFO_SIZE; - if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE)) - return -EFAULT; - offset += AWE_VOICE_REC_SIZE; - - if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { - printk(KERN_WARNING "AWE32 Error: Invalid 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(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", - count, hdr.nvoices); - return -EINVAL; - } - - if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) - return -ENOMEM; - - switch (hdr.write_mode) { - case AWE_WR_EXCLUSIVE: - /* exclusive mode - if the instrument already exists, - return error */ - for (rec = sf->infos; rec; rec = rec->next) { - if (rec->type == V_ST_NORMAL && - rec->bank == hdr.bank && - rec->instr == hdr.instr) - return -EINVAL; - } - break; - case AWE_WR_REPLACE: - /* replace mode - remove the instrument if it already exists */ - remove_info(sf, hdr.bank, hdr.instr); - break; - } - - /* append new layers */ - for (i = 0; i < hdr.nvoices; i++) { - rec = alloc_new_info(); - if (rec == NULL) - return -ENOMEM; - - rec->bank = hdr.bank; - rec->instr = hdr.instr; - rec->type = V_ST_NORMAL; - rec->disabled = FALSE; - - /* copy awe_voice_info parameters */ - if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) { - kfree(rec); - return -EFAULT; - } - offset += AWE_VOICE_INFO_SIZE; -#ifdef AWE_ALLOW_SAMPLE_SHARING - if (sf && sf->shared) { - if (info_duplicated(sf, rec)) { - kfree(rec); - continue; - } - } -#endif /* allow sharing */ - if (rec->v.mode & AWE_MODE_INIT_PARM) - awe_init_voice_parm(&rec->v.parm); - add_sf_info(sf, rec); - awe_set_sample(rec); - add_info_list(rec); - } - - return 0; -} - - -/* remove instrument layers */ -static int -awe_remove_info(awe_patch_info *patch, const char __user *addr, int count) -{ - unsigned char bank, instr; - sf_list *sf; - - if (! patch_opened || (sf = sftail) == NULL) { - printk(KERN_WARNING "AWE32: remove_info: patch not opened\n"); - return -EINVAL; - } - - bank = ((unsigned short)patch->optarg >> 8) & 0xff; - instr = (unsigned short)patch->optarg & 0xff; - if (! remove_info(sf, bank, instr)) - return -EINVAL; - return 0; -} - - -/* load wave sample data */ -static int -awe_load_data(awe_patch_info *patch, const char __user *addr, int count) -{ - int offset, size; - int rc; - awe_sample_info tmprec; - awe_sample_list *rec; - sf_list *sf; - - if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) - return -ENOMEM; - - size = (count - AWE_SAMPLE_INFO_SIZE) / 2; - offset = AWE_PATCH_INFO_SIZE; - if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE)) - return -EFAULT; - offset += AWE_SAMPLE_INFO_SIZE; - if (size != tmprec.size) { - printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n", - tmprec.size, size); - return -EINVAL; - } - - if (search_sample_index(sf, tmprec.sample) != NULL) { -#ifdef AWE_ALLOW_SAMPLE_SHARING - /* if shared sample, skip this data */ - if (sf->type & AWE_PAT_SHARED) - return 0; -#endif /* allow sharing */ - DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample)); - return -EINVAL; - } - - if ((rec = alloc_new_sample()) == NULL) - return -ENOMEM; - - memcpy(&rec->v, &tmprec, sizeof(tmprec)); - - if (rec->v.size > 0) { - if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) { - kfree(rec); - return rc; - } - sf->mem_ptr += rc; - } - - add_sf_sample(sf, rec); - return 0; -} - - -/* replace wave sample data */ -static int -awe_replace_data(awe_patch_info *patch, const char __user *addr, int count) -{ - int offset; - int size; - int rc; - int channels; - awe_sample_info cursmp; - int save_mem_ptr; - sf_list *sf; - awe_sample_list *rec; - - if (! patch_opened || (sf = sftail) == NULL) { - printk(KERN_WARNING "AWE32: replace: patch not opened\n"); - return -EINVAL; - } - - size = (count - AWE_SAMPLE_INFO_SIZE) / 2; - offset = AWE_PATCH_INFO_SIZE; - if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE)) - return -EFAULT; - offset += AWE_SAMPLE_INFO_SIZE; - if (cursmp.size == 0 || size != cursmp.size) { - printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n", - cursmp.size, size); - return -EINVAL; - } - channels = patch->optarg; - if (channels <= 0 || channels > AWE_NORMAL_VOICES) { - printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels); - return -EINVAL; - } - - for (rec = sf->samples; rec; rec = rec->next) { - if (rec->v.sample == cursmp.sample) - break; - } - if (rec == NULL) { - printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n", - cursmp.sample); - return -EINVAL; - } - - if (rec->v.size != cursmp.size) { - printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n", - rec->v.size, cursmp.size); - return -EINVAL; - } - - save_mem_ptr = awe_free_mem_ptr(); - sftail->mem_ptr = rec->v.start - awe_mem_start; - memcpy(&rec->v, &cursmp, sizeof(cursmp)); - rec->v.sf_id = current_sf_id; - if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0) - return rc; - sftail->mem_ptr = save_mem_ptr; - - return 0; -} - - -/*----------------------------------------------------------------*/ - -static const char __user *readbuf_addr; -static int readbuf_offs; -static int readbuf_flags; - -/* initialize read buffer */ -static int -readbuf_init(const char __user *addr, int offset, awe_sample_info *sp) -{ - 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 __user *)(readbuf_addr + readbuf_offs + pos)); - c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */ - } else { - get_user(c, (unsigned short __user *)(readbuf_addr + readbuf_offs + pos * 2)); - } - if (readbuf_flags & AWE_SAMPLE_UNSIGNED) - c ^= 0x8000; /* unsigned -> signed */ - return c; -} - -#define readbuf_word_cache readbuf_word -#define readbuf_end() /**/ - -/*----------------------------------------------------------------*/ - -#define BLANK_LOOP_START 8 -#define BLANK_LOOP_END 40 -#define BLANK_LOOP_SIZE 48 - -/* loading onto memory - return the actual written size */ -static int -awe_write_wave_data(const char __user *addr, int offset, awe_sample_list *list, int channels) -{ - int i, truesize, dram_offset; - awe_sample_info *sp = &list->v; - 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|AWE_SAMPLE_REVERSE_LOOP)) - truesize += sp->loopend - sp->loopstart; - if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) - truesize += BLANK_LOOP_SIZE; - if (awe_free_mem_ptr() + truesize >= memsize/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; - } - } - - awe_close_dram(); - - /* initialize FM */ - awe_init_fm(); - - return truesize; -} - - -/*----------------------------------------------------------------*/ - -#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 __user *addr, int offs, int size, int pmgr_flag) -{ - struct patch_info patch; - awe_voice_info *rec; - awe_sample_info *smp; - awe_voice_list *vrec; - awe_sample_list *smprec; - int sizeof_patch; - int note, rc; - sf_list *sf; - - sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ - if (size < sizeof_patch) { - printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); - return -EINVAL; - } - if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs)) - return -EFAULT; - size -= sizeof_patch; - if (size < patch.len) { - printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n", - size, patch.len); - return -EINVAL; - } - if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL) - return -ENOMEM; - if ((smprec = alloc_new_sample()) == NULL) - return -ENOMEM; - if ((vrec = alloc_new_info()) == NULL) { - kfree(smprec); - return -ENOMEM; - } - - smp = &smprec->v; - smp->sample = sf->num_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, smprec, -1)) < 0) { - kfree(vrec); - return rc; - } - sf->mem_ptr += rc; - add_sf_sample(sf, smprec); - - /* set up voice info */ - rec = &vrec->v; - awe_init_voice_info(rec); - rec->sample = sf->num_info; /* 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_hold(hold) << 8) | - calc_parm_attack(attack); - 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 */ - vrec->bank = ctrls[AWE_MD_GUS_BANK]; - vrec->instr = patch.instr_no; - vrec->disabled = FALSE; - vrec->type = V_ST_NORMAL; - - add_sf_info(sf, vrec); - add_info_list(vrec); - - /* set the voice index */ - awe_set_sample(vrec); - - return 0; -} - -#endif /* AWE_HAS_GUS_COMPATIBILITY */ - -/* - * sample and voice list handlers - */ - -/* append this to the current sf list */ -static void add_sf_info(sf_list *sf, awe_voice_list *rec) -{ - if (sf == NULL) - return; - rec->holder = sf; - rec->v.sf_id = sf->sf_id; - if (sf->last_infos) - sf->last_infos->next = rec; - else - sf->infos = rec; - sf->last_infos = rec; - rec->next = NULL; - sf->num_info++; -} - -/* prepend this sample to sf list */ -static void add_sf_sample(sf_list *sf, awe_sample_list *rec) -{ - if (sf == NULL) - return; - rec->holder = sf; - rec->v.sf_id = sf->sf_id; - if (sf->last_samples) - sf->last_samples->next = rec; - else - sf->samples = rec; - sf->last_samples = rec; - rec->next = NULL; - sf->num_sample++; -} - -/* purge the old records which don't belong with the same file id */ -static void purge_old_list(awe_voice_list *rec, awe_voice_list *next) -{ - rec->next_instr = next; - if (rec->bank == AWE_DRUM_BANK) { - /* remove samples with the same note range */ - awe_voice_list *cur, *prev = rec; - int low = rec->v.low; - int high = rec->v.high; - for (cur = next; cur; cur = cur->next_instr) { - if (cur->v.low == low && - cur->v.high == high && - ! is_identical_holder(cur->holder, rec->holder)) - prev->next_instr = cur->next_instr; - else - prev = cur; - } - } else { - if (! is_identical_holder(next->holder, rec->holder)) - /* remove all samples */ - rec->next_instr = NULL; - } -} - -/* prepend to top of the preset table */ -static void add_info_list(awe_voice_list *rec) -{ - awe_voice_list *prev, *cur; - int key; - - if (rec->disabled) - return; - - key = awe_search_key(rec->bank, rec->instr, rec->v.low); - prev = NULL; - for (cur = preset_table[key]; cur; cur = cur->next_bank) { - /* search the first record with the same bank number */ - if (cur->instr == rec->instr && cur->bank == rec->bank) { - /* replace the list with the new record */ - rec->next_bank = cur->next_bank; - if (prev) - prev->next_bank = rec; - else - preset_table[key] = rec; - purge_old_list(rec, cur); - return; - } - prev = cur; - } - - /* this is the first bank record.. just add this */ - rec->next_instr = NULL; - rec->next_bank = preset_table[key]; - preset_table[key] = rec; -} - -/* remove samples later than the specified sf_id */ -static void -awe_remove_samples(int sf_id) -{ - sf_list *p, *prev; - - if (sf_id <= 0) { - awe_reset_samples(); - return; - } - /* already removed? */ - if (current_sf_id <= sf_id) - return; - - for (p = sftail; p; p = prev) { - if (p->sf_id <= sf_id) - break; - prev = p->prev; - awe_free_sf(p); - } - sftail = p; - if (sftail) { - sf_id = sftail->sf_id; - sftail->next = NULL; - } else { - sf_id = 0; - sfhead = NULL; - } - 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) -{ - sf_list *p; - awe_voice_list *rec; - - memset(preset_table, 0, sizeof(preset_table)); - - for (p = sfhead; p; p = p->next) { - for (rec = p->infos; rec; rec = rec->next) - add_info_list(rec); - } -} - -/* compare the given sf_id pair */ -static int is_identical_holder(sf_list *sf1, sf_list *sf2) -{ - if (sf1 == NULL || sf2 == NULL) - return FALSE; - if (sf1 == sf2) - return TRUE; -#ifdef AWE_ALLOW_SAMPLE_SHARING - { - /* compare with the sharing id */ - sf_list *p; - int counter = 0; - if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */ - sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp; - } - for (p = sf1->shared; p; p = p->shared) { - if (counter++ > current_sf_id) - break; /* strange sharing loop.. quit */ - if (p == sf2) - return TRUE; - } - } -#endif /* allow sharing */ - return FALSE; -} - -/* search the sample index matching with the given sample id */ -static awe_sample_list * -search_sample_index(sf_list *sf, int sample) -{ - awe_sample_list *p; -#ifdef AWE_ALLOW_SAMPLE_SHARING - int counter = 0; - while (sf) { - for (p = sf->samples; p; p = p->next) { - if (p->v.sample == sample) - return p; - } - sf = sf->shared; - if (counter++ > current_sf_id) - break; /* strange sharing loop.. quit */ - } -#else - if (sf) { - for (p = sf->samples; p; p = p->next) { - if (p->v.sample == sample) - return p; - } - } -#endif - return NULL; -} - -/* search the specified sample */ -/* non-zero = found */ -static short -awe_set_sample(awe_voice_list *rec) -{ - awe_sample_list *smp; - awe_voice_info *vp = &rec->v; - - vp->index = 0; - if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL) - return 0; - - /* set the actual sample offsets */ - vp->start += smp->v.start; - vp->end += smp->v.end; - vp->loopstart += smp->v.loopstart; - vp->loopend += smp->v.loopend; - /* copy mode flags */ - vp->mode = smp->v.mode_flags; - /* set flag */ - vp->index = 1; - - return 1; -} - - -/* - * voice allocation - */ - -/* look for all voices associated with the specified note & velocity */ -static int -awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, - awe_voice_info **vlist) -{ - int nvoices; - - nvoices = 0; - for (; rec; rec = rec->next_instr) { - if (note >= rec->v.low && - note <= rec->v.high && - velocity >= rec->v.vellow && - velocity <= rec->v.velhigh) { - if (rec->type == V_ST_MAPPED) { - /* mapper */ - vlist[0] = &rec->v; - return -1; - } - vlist[nvoices++] = &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 bank, int instr, int *note, int velocity, awe_voice_info **vlist) -{ - int nvoices; - awe_voice_list *vrec; - int level = 0; - - for (;;) { - vrec = awe_search_instr(bank, instr, *note); - nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); - if (nvoices == 0) { - if (bank == AWE_DRUM_BANK) - /* search default drumset */ - vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note); - else - /* search default preset */ - vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note); - nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); - } - if (nvoices == 0) { - if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0) - /* search default drumset */ - vrec = awe_search_instr(bank, 0, *note); - else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0) - /* search default preset */ - vrec = awe_search_instr(0, instr, *note); - nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); - } - if (nvoices < 0) { /* mapping */ - int key = vlist[0]->fixkey; - instr = vlist[0]->start; - bank = vlist[0]->end; - if (level++ > 5) { - printk(KERN_ERR "AWE32: too deep mapping level\n"); - return 0; - } - if (key >= 0) - *note = key; - } else - break; - } - - 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, bank; - awe_voice_info *vlist[AWE_MAX_VOICES]; - - if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) - bank = AWE_DRUM_BANK; /* always search drumset */ - else - bank = channels[ch].bank; - - /* check the possible voices; note may be changeable if mapped */ - nvoices = really_alloc_voices(bank, channels[ch].instr, - ¬e, velocity, vlist); - - /* 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 an empty voice. - if no empty voice is found, at least terminate a voice - */ -static int -awe_clear_voice(void) -{ - enum { - OFF=0, RELEASED, SUSTAINED, PLAYING, END - }; - struct voice_candidate_t { - int best; - int time; - int vtarget; - } candidate[END]; - int i, type, vtarget; - - vtarget = 0xffff; - for (type = OFF; type < END; type++) { - candidate[type].best = -1; - candidate[type].time = current_alloc_time + 1; - candidate[type].vtarget = vtarget; - } - - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].state & AWE_ST_OFF) - type = OFF; - else if (voices[i].state & AWE_ST_RELEASED) - type = RELEASED; - else if (voices[i].state & AWE_ST_SUSTAINED) - type = SUSTAINED; - else if (voices[i].state & ~AWE_ST_MARK) - type = PLAYING; - else - continue; -#ifdef AWE_CHECK_VTARGET - /* get current volume */ - vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff; -#endif - if (candidate[type].best < 0 || - vtarget < candidate[type].vtarget || - (vtarget == candidate[type].vtarget && - voices[i].time < candidate[type].time)) { - candidate[type].best = i; - candidate[type].time = voices[i].time; - candidate[type].vtarget = vtarget; - } - } - - for (type = OFF; type < END; type++) { - if ((i = candidate[type].best) >= 0) { - if (voices[i].state != AWE_ST_OFF) - awe_terminate(i); - awe_voice_init(i, TRUE); - return i; - } - } - 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, bank; - awe_voice_info *vlist[AWE_MAX_VOICES]; - - ch = voices[voice].ch; - if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice)) - bank = AWE_DRUM_BANK; /* always search drumset */ - else - bank = voices[voice].cinfo->bank; - - nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr, - ¬e, velocity, vlist); - 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, void __user *arg); - -static int my_mixerdev = -1; - -static struct mixer_operations awe_mixer_operations = { - .owner = THIS_MODULE, - .id = "AWE", - .name = "AWE32 Equalizer", - .ioctl = awe_mixer_ioctl, -}; - -static void __init 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, void __user * arg) -{ - int i, level, value; - - if (((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if (get_user(level, (int __user *)arg)) - return -EFAULT; - level = ((level & 0xff) + (level >> 8)) / 2; - DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); - - if (_SIOC_DIR(cmd) & _SIOC_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; - } - if (put_user(level, (int __user *)arg)) - return -EFAULT; - return level; -} -#endif /* CONFIG_AWE32_MIXER */ - - -/* - * initialization of Emu8000 - */ - -/* 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 (memsize <= 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; - } - printk("awe: not ready to write..\n"); - return -EPERM; - } - - /* 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; - } - } -} - - -/* - * check dram size on AWE board - */ - -/* any three numbers you like */ -#define UNIQUE_ID1 0x1234 -#define UNIQUE_ID2 0x4321 -#define UNIQUE_ID3 0xABCD - -static void __init -awe_check_dram(void) -{ - if (awe_present) /* already initialized */ - return; - - if (memsize >= 0) { /* given by config file or module option */ - memsize *= 1024; /* convert to Kbytes */ - return; - } - - awe_open_dram_for_check(); - - memsize = 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 (memsize < 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; - memsize += 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 + memsize*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 + memsize*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", memsize)); - - /* convert to Kbytes */ - memsize *= 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 __user *addr, int count) -{ - if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { - printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg); - return -EINVAL; - } - if (count < sizeof(awe_chorus_fx_rec)) { - printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n"); - return -EINVAL; - } - if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, - sizeof(awe_chorus_fx_rec))) - return -EFAULT; - 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 __user *addr, int count) -{ - if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { - printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg); - return -EINVAL; - } - if (count < sizeof(awe_reverb_fx_rec)) { - printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n"); - return -EINVAL; - } - if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, - sizeof(awe_reverb_fx_rec))) - return -EFAULT; - 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, void __user * 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 = -{ - .owner = THIS_MODULE, - .info = {"AWE Midi Emu", 0, 0, SNDCARD_SB}, - .in_info = {0}, - .open = awe_midi_open, /*open*/ - .close = awe_midi_close, /*close*/ - .ioctl = awe_midi_ioctl, /*ioctl*/ - .outputc = awe_midi_outputc, /*outputc*/ -}; - -static int my_mididev = -1; - -static void __init 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, finetune; - -static int xg_mapping = TRUE; -static int xg_bankmode; - -/* 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, void __user *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 */ - - -/*----------------------------------------------------------------*/ - - -/* - * initialization of AWE driver - */ - -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(); -} - - -/* - * Core Device Management Functions - */ - -/* 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; -} - -/* - * port request - * 0x620-623, 0xA20-A23, 0xE20-E23 - */ - -static int -awe_request_region(void) -{ - if (! port_setuped) - return 0; - if (! request_region(awe_ports[0], 4, "sound driver (AWE32)")) - return 0; - if (! request_region(awe_ports[1], 4, "sound driver (AWE32)")) - goto err_out; - if (! request_region(awe_ports[3], 4, "sound driver (AWE32)")) - goto err_out1; - return 1; -err_out1: - release_region(awe_ports[1], 4); -err_out: - release_region(awe_ports[0], 4); - return 0; -} - -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); -} - -static int awe_attach_device(void) -{ - if (awe_present) return 0; /* for OSS38.. called twice? */ - - /* reserve I/O ports for awedrv */ - if (! awe_request_region()) { - printk(KERN_ERR "AWE32: I/O area already used.\n"); - return 0; - } - - /* set buffers to NULL */ - sfhead = sftail = NULL; - - my_dev = sound_alloc_synthdev(); - if (my_dev == -1) { - printk(KERN_ERR "AWE32 Error: too many synthesizers\n"); - awe_release_region(); - 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 - - /* clear all samples */ - awe_reset_samples(); - - /* initialize AWE32 hardware */ - awe_initialize(); - - sprintf(awe_info.name, "AWE32-%s (RAM%dk)", - AWEDRV_VERSION, memsize/1024); - printk(KERN_INFO "\n", memsize/1024); - - awe_present = TRUE; - - return 1; -} - -static void awe_dettach_device(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; - } -} - - -/* - * Legacy device Probing - */ - -/* detect emu8000 chip on the specified address; from VV's guide */ - -static int __init -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 __init awe_detect_legacy_devices(void) -{ - int base; - for (base = 0x620; base <= 0x680; base += 0x20) - if (awe_detect_base(base)) { - awe_attach_device(); - return 1; - } - DEBUG(0,printk("AWE32 Legacy detection failed\n")); - return 0; -} - - -/* - * PnP device Probing - */ - -static struct pnp_device_id awe_pnp_ids[] = { - {.id = "CTL0021", .driver_data = 0}, /* AWE32 WaveTable */ - {.id = "CTL0022", .driver_data = 0}, /* AWE64 WaveTable */ - {.id = "CTL0023", .driver_data = 0}, /* AWE64 Gold WaveTable */ - { } /* terminator */ -}; - -MODULE_DEVICE_TABLE(pnp, awe_pnp_ids); - -static int awe_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) -{ - int io1, io2, io3; - - if (awe_present) { - printk(KERN_ERR "AWE32: This driver only supports one AWE32 device, skipping.\n"); - } - - if (!pnp_port_valid(dev,0) || - !pnp_port_valid(dev,1) || - !pnp_port_valid(dev,2)) { - printk(KERN_ERR "AWE32: The PnP device does not have the required resources.\n"); - return -EINVAL; - } - io1 = pnp_port_start(dev,0); - io2 = pnp_port_start(dev,1); - io3 = pnp_port_start(dev,2); - printk(KERN_INFO "AWE32: A PnP Wave Table was detected at IO's %#x,%#x,%#x.\n", - io1, io2, io3); - setup_ports(io1, io2, io3); - - awe_attach_device(); - return 0; -} - -static void awe_pnp_remove(struct pnp_dev *dev) -{ - awe_dettach_device(); -} - -static struct pnp_driver awe_pnp_driver = { - .name = "AWE32", - .id_table = awe_pnp_ids, - .probe = awe_pnp_probe, - .remove = awe_pnp_remove, -}; - -static int __init awe_detect_pnp_devices(void) -{ - int ret; - - ret = pnp_register_driver(&awe_pnp_driver); - if (ret<0) - printk(KERN_ERR "AWE32: PnP support is unavailable.\n"); - return ret; -} - - -/* - * device / lowlevel (module) interface - */ - -static int __init -awe_detect(void) -{ - printk(KERN_INFO "AWE32: Probing for WaveTable...\n"); - if (isapnp) { - if (awe_detect_pnp_devices()>=0) - return 1; - } else - printk(KERN_INFO "AWE32: Skipping PnP detection.\n"); - - if (awe_detect_legacy_devices()) - return 1; - - return 0; -} - -static int __init attach_awe(void) -{ - return awe_detect() ? 0 : -ENODEV; -} - -static void __exit unload_awe(void) -{ - pnp_unregister_driver(&awe_pnp_driver); - awe_dettach_device(); -} - - -module_init(attach_awe); -module_exit(unload_awe); - -#ifndef MODULE -static int __init setup_awe(char *str) -{ - /* io, memsize, isapnp */ - int ints[4]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - memsize = ints[2]; - isapnp = ints[3]; - - return 1; -} - -__setup("awe=", setup_awe); -#endif diff --git a/sound/oss/awe_wave.h b/sound/oss/awe_wave.h deleted file mode 100644 index fe58481060..0000000000 --- a/sound/oss/awe_wave.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * sound/oss/awe_wave.h - * - * Configuration of AWE32/SB32/AWE64 wave table synth driver. - * version 0.4.4; Jan. 4, 2000 - * - * 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 */ - -/* - * AWE driver version number - */ -#define AWE_MAJOR_VERSION 0 -#define AWE_MINOR_VERSION 4 -#define AWE_TINY_VERSION 4 -#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION) -#define AWEDRV_VERSION "0.4.4" diff --git a/sound/oss/cmpci.c b/sound/oss/cmpci.c deleted file mode 100644 index ea51aafaf4..0000000000 --- a/sound/oss/cmpci.c +++ /dev/null @@ -1,3381 +0,0 @@ -/* - * cmpci.c -- C-Media PCI audio driver. - * - * Copyright (C) 1999 C-media support (support@cmedia.com.tw) - * - * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * For update, visit: - * http://www.cmedia.com.tw - * - * 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. - * - * Special thanks to David C. Niemi, Jan Pfeifer - * - * - * Module command line parameters: - * none so far - * - * - * Supported devices: - * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * /dev/midi simple MIDI UART interface, no ioctl - * - * The card has both an FM and a Wavetable synth, but I have to figure - * out first how to drive them... - * - * Revision history - * 06.05.98 0.1 Initial release - * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation - * First stab at a simple midi interface (no bells&whistles) - * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of - * set_dac_rate in the FMODE_WRITE case in cm_open - * Fix hwptr out of bounds (now mpg123 works) - * 14.05.98 0.4 Don't allow excessive interrupt rates - * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.98 0.6 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.98 0.7 Fix realplayer problems - dac.count issues - * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs - * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 18.08.99 1.5 Only deallocate DMA buffer when unloading. - * 02.09.99 1.6 Enable SPDIF LOOP - * Change the mixer read back - * 21.09.99 2.33 Use RCS version as driver version. - * Add support for modem, S/PDIF loop and 4 channels. - * (8738 only) - * Fix bug cause x11amp cannot play. - * - * Fixes: - * Arnaldo Carvalho de Melo - * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it - * was calling prog_dmabuf with s->lock held, call missing - * unlock_kernel in cm_midi_release - * 08/10/2001 - use set_current_state in some more places - * - * Carlos Eduardo Gorges - * Fri May 25 2001 - * - SMP support ( spin[un]lock* revision ) - * - speaker mixer support - * Mon Aug 13 2001 - * - optimizations and cleanups - * - * 03/01/2003 - open_mode fixes from Georg Acher - * Simon Braunschmidt - * Sat Jan 31 2004 - * - provide support for opl3 FM by releasing IO range after initialization - * - * ChenLi Tien - * Mar 9 2004 - * - Fix S/PDIF out if spdif_loop enabled - * - Load opl3 driver if enabled (fmio in proper range) - * - Load mpu401 if enabled (mpuio in proper range) - * Apr 5 2004 - * - Fix DUAL_DAC dma synchronization bug - * - Check exist FM/MPU401 I/O before activate. - * - Add AFTM_S16_BE format support, so MPlayer/Xine can play AC3/mutlichannel - * on Mac - * - Change to support kernel 2.6 so only small patch needed - * - All parameters default to 0 - * - Add spdif_out to send PCM through S/PDIF out jack - * - Add hw_copy to get 4-spaker output for general PCM/analog output - * - * Stefan Thater - * Apr 5 2004 - * - Fix mute single channel for CD/Line-in/AUX-in - */ -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef CONFIG_SOUND_CMPCI_MIDI -#include "sound_config.h" -#include "mpu401.h" -#endif -#ifdef CONFIG_SOUND_CMPCI_FM -#include "opl3.h" -#endif -#ifdef CONFIG_SOUND_CMPCI_JOYSTICK -#include -#include - -#endif - -/* --------------------------------------------------------------------- */ -#undef OSS_DOCUMENTED_MIXER_SEMANTICS -#undef DMABYTEIO -#define DBG(x) {} -/* --------------------------------------------------------------------- */ - -#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A) - -/* CM8338 registers definition ****************/ - -#define CODEC_CMI_FUNCTRL0 (0x00) -#define CODEC_CMI_FUNCTRL1 (0x04) -#define CODEC_CMI_CHFORMAT (0x08) -#define CODEC_CMI_INT_HLDCLR (0x0C) -#define CODEC_CMI_INT_STATUS (0x10) -#define CODEC_CMI_LEGACY_CTRL (0x14) -#define CODEC_CMI_MISC_CTRL (0x18) -#define CODEC_CMI_TDMA_POS (0x1C) -#define CODEC_CMI_MIXER (0x20) -#define CODEC_SB16_DATA (0x22) -#define CODEC_SB16_ADDR (0x23) -#define CODEC_CMI_MIXER1 (0x24) -#define CODEC_CMI_MIXER2 (0x25) -#define CODEC_CMI_AUX_VOL (0x26) -#define CODEC_CMI_MISC (0x27) -#define CODEC_CMI_AC97 (0x28) - -#define CODEC_CMI_CH0_FRAME1 (0x80) -#define CODEC_CMI_CH0_FRAME2 (0x84) -#define CODEC_CMI_CH1_FRAME1 (0x88) -#define CODEC_CMI_CH1_FRAME2 (0x8C) - -#define CODEC_CMI_SPDIF_CTRL (0x90) -#define CODEC_CMI_MISC_CTRL2 (0x92) - -#define CODEC_CMI_EXT_REG (0xF0) - -/* Mixer registers for SB16 ******************/ - -#define DSP_MIX_DATARESETIDX ((unsigned char)(0x00)) - -#define DSP_MIX_MASTERVOLIDX_L ((unsigned char)(0x30)) -#define DSP_MIX_MASTERVOLIDX_R ((unsigned char)(0x31)) -#define DSP_MIX_VOICEVOLIDX_L ((unsigned char)(0x32)) -#define DSP_MIX_VOICEVOLIDX_R ((unsigned char)(0x33)) -#define DSP_MIX_FMVOLIDX_L ((unsigned char)(0x34)) -#define DSP_MIX_FMVOLIDX_R ((unsigned char)(0x35)) -#define DSP_MIX_CDVOLIDX_L ((unsigned char)(0x36)) -#define DSP_MIX_CDVOLIDX_R ((unsigned char)(0x37)) -#define DSP_MIX_LINEVOLIDX_L ((unsigned char)(0x38)) -#define DSP_MIX_LINEVOLIDX_R ((unsigned char)(0x39)) - -#define DSP_MIX_MICVOLIDX ((unsigned char)(0x3A)) -#define DSP_MIX_SPKRVOLIDX ((unsigned char)(0x3B)) - -#define DSP_MIX_OUTMIXIDX ((unsigned char)(0x3C)) - -#define DSP_MIX_ADCMIXIDX_L ((unsigned char)(0x3D)) -#define DSP_MIX_ADCMIXIDX_R ((unsigned char)(0x3E)) - -#define DSP_MIX_INGAINIDX_L ((unsigned char)(0x3F)) -#define DSP_MIX_INGAINIDX_R ((unsigned char)(0x40)) -#define DSP_MIX_OUTGAINIDX_L ((unsigned char)(0x41)) -#define DSP_MIX_OUTGAINIDX_R ((unsigned char)(0x42)) - -#define DSP_MIX_AGCIDX ((unsigned char)(0x43)) - -#define DSP_MIX_TREBLEIDX_L ((unsigned char)(0x44)) -#define DSP_MIX_TREBLEIDX_R ((unsigned char)(0x45)) -#define DSP_MIX_BASSIDX_L ((unsigned char)(0x46)) -#define DSP_MIX_BASSIDX_R ((unsigned char)(0x47)) -#define DSP_MIX_EXTENSION ((unsigned char)(0xf0)) -// pseudo register for AUX -#define DSP_MIX_AUXVOL_L ((unsigned char)(0x50)) -#define DSP_MIX_AUXVOL_R ((unsigned char)(0x51)) - -// I/O length -#define CM_EXTENT_CODEC 0x100 -#define CM_EXTENT_MIDI 0x2 -#define CM_EXTENT_SYNTH 0x4 -#define CM_EXTENT_GAME 0x8 - -// Function Control Register 0 (00h) -#define CHADC0 0x01 -#define CHADC1 0x02 -#define PAUSE0 0x04 -#define PAUSE1 0x08 - -// Function Control Register 0+2 (02h) -#define CHEN0 0x01 -#define CHEN1 0x02 -#define RST_CH0 0x04 -#define RST_CH1 0x08 - -// Function Control Register 1 (04h) -#define JYSTK_EN 0x02 -#define UART_EN 0x04 -#define SPDO2DAC 0x40 -#define SPDFLOOP 0x80 - -// Function Control Register 1+1 (05h) -#define SPDF_0 0x01 -#define SPDF_1 0x02 -#define ASFC 0x1c -#define DSFC 0xe0 -#define SPDIF2DAC (SPDF_1 << 8 | SPDO2DAC) - -// Channel Format Register (08h) -#define CM_CFMT_STEREO 0x01 -#define CM_CFMT_16BIT 0x02 -#define CM_CFMT_MASK 0x03 -#define POLVALID 0x20 -#define INVSPDIFI 0x80 - -// Channel Format Register+2 (0ah) -#define SPD24SEL 0x20 - -// Channel Format Register+3 (0bh) -#define CHB3D 0x20 -#define CHB3D5C 0x80 - -// Interrupt Hold/Clear Register+2 (0eh) -#define CH0_INT_EN 0x01 -#define CH1_INT_EN 0x02 - -// Interrupt Register (10h) -#define CHINT0 0x01 -#define CHINT1 0x02 -#define CH0BUSY 0x04 -#define CH1BUSY 0x08 - -// Legacy Control/Status Register+1 (15h) -#define EXBASEN 0x10 -#define BASE2LIN 0x20 -#define CENTR2LIN 0x40 -#define CB2LIN (BASE2LIN | CENTR2LIN) -#define CHB3D6C 0x80 - -// Legacy Control/Status Register+2 (16h) -#define DAC2SPDO 0x20 -#define SPDCOPYRHT 0x40 -#define ENSPDOUT 0x80 - -// Legacy Control/Status Register+3 (17h) -#define FMSEL 0x03 -#define VSBSEL 0x0c -#define VMPU 0x60 -#define NXCHG 0x80 - -// Miscellaneous Control Register (18h) -#define REAR2LIN 0x20 -#define MUTECH1 0x40 -#define ENCENTER 0x80 - -// Miscellaneous Control Register+1 (19h) -#define SELSPDIFI2 0x01 -#define SPDF_AC97 0x80 - -// Miscellaneous Control Register+2 (1ah) -#define AC3_EN 0x04 -#define FM_EN 0x08 -#define SPD32SEL 0x20 -#define XCHGDAC 0x40 -#define ENDBDAC 0x80 - -// Miscellaneous Control Register+3 (1bh) -#define SPDIFI48K 0x01 -#define SPDO5V 0x02 -#define N4SPK3D 0x04 -#define RESET 0x40 -#define PWD 0x80 -#define SPDIF48K (SPDIFI48K << 24 | SPDF_AC97 << 8) - -// Mixer1 (24h) -#define CDPLAY 0x01 -#define X3DEN 0x02 -#define REAR2FRONT 0x10 -#define SPK4 0x20 -#define WSMUTE 0x40 -#define FMMUTE 0x80 - -// Miscellaneous Register (27h) -#define SPDVALID 0x02 -#define CENTR2MIC 0x04 - -// Miscellaneous Register2 (92h) -#define SPD32KFMT 0x10 - -#define CM_CFMT_DACSHIFT 2 -#define CM_CFMT_ADCSHIFT 0 -#define CM_FREQ_DACSHIFT 5 -#define CM_FREQ_ADCSHIFT 2 -#define RSTDAC RST_CH1 -#define RSTADC RST_CH0 -#define ENDAC CHEN1 -#define ENADC CHEN0 -#define PAUSEDAC PAUSE1 -#define PAUSEADC PAUSE0 -#define CODEC_CMI_ADC_FRAME1 CODEC_CMI_CH0_FRAME1 -#define CODEC_CMI_ADC_FRAME2 CODEC_CMI_CH0_FRAME2 -#define CODEC_CMI_DAC_FRAME1 CODEC_CMI_CH1_FRAME1 -#define CODEC_CMI_DAC_FRAME2 CODEC_CMI_CH1_FRAME2 -#define DACINT CHINT1 -#define ADCINT CHINT0 -#define DACBUSY CH1BUSY -#define ADCBUSY CH0BUSY -#define ENDACINT CH1_INT_EN -#define ENADCINT CH0_INT_EN - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -#define SND_DEV_DSP16 5 - -#define NR_DEVICE 3 /* maximum number of devices */ - -#define set_dac1_rate set_adc_rate -#define set_dac1_rate_unlocked set_adc_rate_unlocked -#define stop_dac1 stop_adc -#define stop_dac1_unlocked stop_adc_unlocked -#define get_dmadac1 get_dmaadc - -static unsigned int devindex = 0; - -//*********************************************/ - -struct cm_state { - /* magic */ - unsigned int magic; - - /* list of cmedia devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - int dev_audio; /* soundcore stuff */ - int dev_mixer; - - unsigned int iosb, iobase, iosynth, - iomidi, iogame, irq; /* hardware resources */ - unsigned short deviceid; /* pci_id */ - - struct { /* mixer stuff */ - unsigned int modcnt; - unsigned short vol[13]; - } mix; - - unsigned int rateadc, ratedac; /* wave stuff */ - unsigned char fmt, enable; - - spinlock_t lock; - struct mutex open_mutex; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - - unsigned fragsize; /* redundant, but makes calculations easier */ - unsigned dmasize; - unsigned fragsamples; - unsigned dmasamples; - - unsigned mapped:1; /* OSS stuff */ - unsigned ready:1; - unsigned endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - -#ifdef CONFIG_SOUND_CMPCI_MIDI - int midi_devc; - struct address_info mpu_data; -#endif -#ifdef CONFIG_SOUND_CMPCI_JOYSTICK - struct gameport *gameport; -#endif - - int chip_version; - int max_channels; - int curr_channels; - int capability; /* HW capability, various for chip versions */ - - int status; /* HW or SW state */ - - int spdif_counter; /* spdif frame counter */ -}; - -/* flags used for capability */ -#define CAN_AC3_HW 0x00000001 /* 037 or later */ -#define CAN_AC3_SW 0x00000002 /* 033 or later */ -#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW) -#define CAN_DUAL_DAC 0x00000004 /* 033 or later */ -#define CAN_MULTI_CH_HW 0x00000008 /* 039 or later */ -#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC) -#define CAN_LINE_AS_REAR 0x00000010 /* 033 or later */ -#define CAN_LINE_AS_BASS 0x00000020 /* 039 or later */ -#define CAN_MIC_AS_BASS 0x00000040 /* 039 or later */ - -/* flags used for status */ -#define DO_AC3_HW 0x00000001 -#define DO_AC3_SW 0x00000002 -#define DO_AC3 (DO_AC3_HW | DO_AC3_SW) -#define DO_DUAL_DAC 0x00000004 -#define DO_MULTI_CH_HW 0x00000008 -#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC) -#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */ -#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */ -#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */ -#define DO_SPDIF_OUT 0x00000100 -#define DO_SPDIF_IN 0x00000200 -#define DO_SPDIF_LOOP 0x00000400 -#define DO_BIGENDIAN_W 0x00001000 /* used in PowerPC */ -#define DO_BIGENDIAN_R 0x00002000 /* used in PowerPC */ - -static LIST_HEAD(devs); - -static int mpuio; -static int fmio; -static int joystick; -static int spdif_inverse; -static int spdif_loop; -static int spdif_out; -static int use_line_as_rear; -static int use_line_as_bass; -static int use_mic_as_bass; -static int mic_boost; -static int hw_copy; -module_param(mpuio, int, 0); -module_param(fmio, int, 0); -module_param(joystick, bool, 0); -module_param(spdif_inverse, bool, 0); -module_param(spdif_loop, bool, 0); -module_param(spdif_out, bool, 0); -module_param(use_line_as_rear, bool, 0); -module_param(use_line_as_bass, bool, 0); -module_param(use_mic_as_bass, bool, 0); -module_param(mic_boost, bool, 0); -module_param(hw_copy, bool, 0); -MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable"); -MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable"); -MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver"); -MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal"); -MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly"); -MODULE_PARM_DESC(spdif_out, "(1/0) Send PCM to S/PDIF-out (PCM volume will not function)"); -MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out"); -MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center"); -MODULE_PARM_DESC(use_mic_as_bass, "(1/0) Use mic-in jack as bass/center"); -MODULE_PARM_DESC(mic_boost, "(1/0) Enable microphone boost"); -MODULE_PARM_DESC(hw_copy, "Copy front channel to surround channel"); - -/* --------------------------------------------------------------------- */ - -static inline unsigned ld2(unsigned int x) -{ - unsigned exp=16,l=5,r=0; - static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; - - /* num: 2, 4, 16, 256, 65536 */ - /* exp: 1, 2, 4, 8, 16 */ - - while(l--) { - if( x >= num[l] ) { - if(num[l]>2) x >>= exp; - r+=exp; - } - exp>>=1; - } - - return r; -} - -/* --------------------------------------------------------------------- */ - -static void maskb(unsigned int addr, unsigned int mask, unsigned int value) -{ - outb((inb(addr) & mask) | value, addr); -} - -static void maskw(unsigned int addr, unsigned int mask, unsigned int value) -{ - outw((inw(addr) & mask) | value, addr); -} - -static void maskl(unsigned int addr, unsigned int mask, unsigned int value) -{ - outl((inl(addr) & mask) | value, addr); -} - -static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count) -{ - if (addr) - outl(addr, s->iobase + CODEC_CMI_ADC_FRAME1); - outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2); - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC0, 0); -} - -static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) -{ - outl(addr, s->iobase + CODEC_CMI_ADC_FRAME1); - outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2); - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, CHADC0); -} - -static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) -{ - outl(addr, s->iobase + CODEC_CMI_DAC_FRAME1); - outw(count - 1, s->iobase + CODEC_CMI_DAC_FRAME2); - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC1, 0); - if (s->status & DO_DUAL_DAC) - set_dmadac1(s, 0, count); -} - -static void set_countadc(struct cm_state *s, unsigned count) -{ - outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2 + 2); -} - -static void set_countdac(struct cm_state *s, unsigned count) -{ - outw(count - 1, s->iobase + CODEC_CMI_DAC_FRAME2 + 2); - if (s->status & DO_DUAL_DAC) - set_countadc(s, count); -} - -static unsigned get_dmadac(struct cm_state *s) -{ - unsigned int curr_addr; - - curr_addr = inw(s->iobase + CODEC_CMI_DAC_FRAME2) + 1; - curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; - curr_addr = s->dma_dac.dmasize - curr_addr; - - return curr_addr; -} - -static unsigned get_dmaadc(struct cm_state *s) -{ - unsigned int curr_addr; - - curr_addr = inw(s->iobase + CODEC_CMI_ADC_FRAME2) + 1; - curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; - curr_addr = s->dma_adc.dmasize - curr_addr; - - return curr_addr; -} - -static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data) -{ - unsigned char regval, pseudo; - - // pseudo register - if (idx == DSP_MIX_AUXVOL_L) { - data >>= 4; - data &= 0x0f; - regval = inb(s->iobase + CODEC_CMI_AUX_VOL) & ~0x0f; - outb(regval | data, s->iobase + CODEC_CMI_AUX_VOL); - return; - } - if (idx == DSP_MIX_AUXVOL_R) { - data &= 0xf0; - regval = inb(s->iobase + CODEC_CMI_AUX_VOL) & ~0xf0; - outb(regval | data, s->iobase + CODEC_CMI_AUX_VOL); - return; - } - outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); - // pseudo bits - if (idx == DSP_MIX_OUTMIXIDX) { - pseudo = data & ~0x1f; - pseudo >>= 1; - regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x30; - outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); - } - if (idx == DSP_MIX_ADCMIXIDX_L) { - pseudo = data & 0x80; - pseudo >>= 1; - regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x40; - outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); - } - if (idx == DSP_MIX_ADCMIXIDX_R) { - pseudo = data & 0x80; - regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x80; - outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); - } - outb(data, s->iobase + CODEC_SB16_DATA); - udelay(10); -} - -static unsigned char rdmixer(struct cm_state *s, unsigned char idx) -{ - unsigned char v, pseudo; - - // pseudo register - if (idx == DSP_MIX_AUXVOL_L) { - v = inb(s->iobase + CODEC_CMI_AUX_VOL) & 0x0f; - v <<= 4; - return v; - } - if (idx == DSP_MIX_AUXVOL_L) { - v = inb(s->iobase + CODEC_CMI_AUX_VOL) & 0xf0; - return v; - } - outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); - v = inb(s->iobase + CODEC_SB16_DATA); - udelay(10); - // pseudo bits - if (idx == DSP_MIX_OUTMIXIDX) { - pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x30; - pseudo <<= 1; - v |= pseudo; - } - if (idx == DSP_MIX_ADCMIXIDX_L) { - pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x40; - pseudo <<= 1; - v |= pseudo; - } - if (idx == DSP_MIX_ADCMIXIDX_R) { - pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x80; - v |= pseudo; - } - return v; -} - -static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data) -{ - if (mask && s->chip_version > 0) { /* 8338 cannot keep this */ - s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); - udelay(10); - } - s->fmt = (s->fmt & mask) | data; - outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); - udelay(10); -} - -static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_fmt_unlocked(s,mask,data); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data) -{ - outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); - outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); - udelay(10); -} - -static struct { - unsigned rate; - unsigned lower; - unsigned upper; - unsigned char freq; -} rate_lookup[] = -{ - { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 }, - { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 }, - { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 }, - { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 }, - { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, - { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, - { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, - { 48000, (44100 + 48000) / 2, 48000, 7 } -}; - -static void set_spdif_copyright(struct cm_state *s, int spdif_copyright) -{ - /* enable SPDIF-in Copyright */ - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~SPDCOPYRHT, spdif_copyright ? SPDCOPYRHT : 0); -} - -static void set_spdif_loop(struct cm_state *s, int spdif_loop) -{ - /* enable SPDIF loop */ - if (spdif_loop) { - s->status |= DO_SPDIF_LOOP; - /* turn on spdif-in to spdif-out */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, SPDFLOOP); - } else { - s->status &= ~DO_SPDIF_LOOP; - /* turn off spdif-in to spdif-out */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~SPDFLOOP, 0); - } -} - -static void set_spdif_monitor(struct cm_state *s, int channel) -{ - // SPDO2DAC - maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~SPDO2DAC, channel == 2 ? SPDO2DAC : 0); - // CDPLAY - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~CDPLAY, channel ? CDPLAY : 0); -} - -static void set_spdifout_level(struct cm_state *s, int level5v) -{ - /* SPDO5V */ - if (s->chip_version > 0) - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~SPDO5V, level5v ? SPDO5V : 0); -} - -static void set_spdifin_inverse(struct cm_state *s, int spdif_inverse) -{ - if (s->chip_version == 0) /* 8338 has not this feature */ - return; - if (spdif_inverse) { - /* turn on spdif-in inverse */ - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_CHFORMAT, ~0, INVSPDIFI); - else - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); - } else { - /* turn off spdif-ininverse */ - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_CHFORMAT, ~INVSPDIFI, 0); - else - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); - } -} - -static void set_spdifin_channel2(struct cm_state *s, int channel2) -{ - /* SELSPDIFI2 */ - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 1, ~SELSPDIFI2, channel2 ? SELSPDIFI2 : 0); -} - -static void set_spdifin_valid(struct cm_state *s, int valid) -{ - /* SPDVALID */ - maskb(s->iobase + CODEC_CMI_MISC, ~SPDVALID, valid ? SPDVALID : 0); -} - -static void set_spdifout_unlocked(struct cm_state *s, unsigned rate) -{ - if (rate != 48000 && rate != 44100) - rate = 0; - if (rate == 48000 || rate == 44100) { - set_spdif_loop(s, 0); - // SPDF_1 - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0, SPDF_1); - // SPDIFI48K SPDF_AC97 - maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~SPDIF48K, rate == 48000 ? SPDIF48K : 0); - if (s->chip_version >= 55) - // SPD32KFMT - maskb(s->iobase + CODEC_CMI_MISC_CTRL2, ~SPD32KFMT, rate == 48000 ? SPD32KFMT : 0); - if (s->chip_version > 0) - // ENSPDOUT - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, ENSPDOUT); - // monitor SPDIF out - set_spdif_monitor(s, 2); - s->status |= DO_SPDIF_OUT; - } else { - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~SPDF_1, 0); - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~ENSPDOUT, 0); - // monitor none - set_spdif_monitor(s, 0); - s->status &= ~DO_SPDIF_OUT; - } -} - -static void set_spdifout(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_spdifout_unlocked(s,rate); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void set_spdifin_unlocked(struct cm_state *s, unsigned rate) -{ - if (rate == 48000 || rate == 44100) { - // SPDF_1 - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0, SPDF_1); - // SPDIFI48K SPDF_AC97 - maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~SPDIF48K, rate == 48000 ? SPDIF48K : 0); - s->status |= DO_SPDIF_IN; - } else { - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~SPDF_1, 0); - s->status &= ~DO_SPDIF_IN; - } -} - -static void set_spdifin(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_spdifin_unlocked(s,rate); - spin_unlock_irqrestore(&s->lock, flags); -} - -/* find parity for bit 4~30 */ -static unsigned parity(unsigned data) -{ - unsigned parity = 0; - int counter = 4; - - data >>= 4; // start from bit 4 - while (counter <= 30) { - if (data & 1) - parity++; - data >>= 1; - counter++; - } - return parity & 1; -} - -static void set_ac3_unlocked(struct cm_state *s, unsigned rate) -{ - if (!(s->capability & CAN_AC3)) - return; - /* enable AC3 */ - if (rate && rate != 44100) - rate = 48000; - if (rate == 48000 || rate == 44100) { - // mute DAC - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, WSMUTE); - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0, MUTECH1); - // AC3EN for 039, 0x04 - if (s->chip_version >= 39) { - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, AC3_EN); - if (s->chip_version == 55) - maskb(s->iobase + CODEC_CMI_SPDIF_CTRL, ~2, 0); - // AC3EN for 037, 0x10 - } else if (s->chip_version == 37) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); - if (s->capability & CAN_AC3_HW) { - // SPD24SEL for 039, 0x20, but cannot be set - if (s->chip_version == 39) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, SPD24SEL); - // SPD24SEL for 037, 0x02 - else if (s->chip_version == 37) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~CDPLAY, 0); - - s->status |= DO_AC3_HW; - } else { - // SPD32SEL for 037 & 039 - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, SPD32SEL); - // set 176K sample rate to fix 033 HW bug - if (s->chip_version == 33) { - if (rate == 48000) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); - else - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); - } - s->status |= DO_AC3_SW; - } - } else { - maskb(s->iobase + CODEC_CMI_MIXER1, ~WSMUTE, 0); - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~MUTECH1, 0); - maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~(SPD24SEL|0x12), 0); - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~(SPD32SEL|AC3_EN), 0); - if (s->chip_version == 33) - maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); - if (s->chip_version >= 39) - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, CDPLAY); - s->status &= ~DO_AC3; - } - s->spdif_counter = 0; -} - -static void set_line_as_rear(struct cm_state *s, int use_line_as_rear) -{ - if (!(s->capability & CAN_LINE_AS_REAR)) - return; - if (use_line_as_rear) { - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, SPK4); - s->status |= DO_LINE_AS_REAR; - } else { - maskb(s->iobase + CODEC_CMI_MIXER1, ~SPK4, 0); - s->status &= ~DO_LINE_AS_REAR; - } -} - -static void set_line_as_bass(struct cm_state *s, int use_line_as_bass) -{ - if (!(s->capability & CAN_LINE_AS_BASS)) - return; - if (use_line_as_bass) { - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, CB2LIN); - s->status |= DO_LINE_AS_BASS; - } else { - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CB2LIN, 0); - s->status &= ~DO_LINE_AS_BASS; - } -} - -static void set_mic_as_bass(struct cm_state *s, int use_mic_as_bass) -{ - if (!(s->capability & CAN_MIC_AS_BASS)) - return; - if (use_mic_as_bass) { - maskb(s->iobase + CODEC_CMI_MISC, ~0, 0x04); - s->status |= DO_MIC_AS_BASS; - } else { - maskb(s->iobase + CODEC_CMI_MISC, ~0x04, 0); - s->status &= ~DO_MIC_AS_BASS; - } -} - -static void set_hw_copy(struct cm_state *s, int hw_copy) -{ - if (s->max_channels > 2 && hw_copy) - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, N4SPK3D); - else - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~N4SPK3D, 0); -} - -static void set_ac3(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - set_spdifout_unlocked(s, rate); - set_ac3_unlocked(s, rate); - spin_unlock_irqrestore(&s->lock, flags); -} - -static int trans_ac3(struct cm_state *s, void *dest, const char __user *source, int size) -{ - int i = size / 2; - unsigned long data; - unsigned short data16; - unsigned long *dst = (unsigned long *) dest; - unsigned short __user *src = (unsigned short __user *)source; - int err; - - do { - if ((err = __get_user(data16, src++))) - return err; - data = (unsigned long)le16_to_cpu(data16); - data <<= 12; // ok for 16-bit data - if (s->spdif_counter == 2 || s->spdif_counter == 3) - data |= 0x40000000; // indicate AC-3 raw data - if (parity(data)) - data |= 0x80000000; // parity - if (s->spdif_counter == 0) - data |= 3; // preamble 'M' - else if (s->spdif_counter & 1) - data |= 5; // odd, 'W' - else - data |= 9; // even, 'M' - *dst++ = cpu_to_le32(data); - s->spdif_counter++; - if (s->spdif_counter == 384) - s->spdif_counter = 0; - } while (--i); - - return 0; -} - -static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate) -{ - unsigned char freq = 4; - int i; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { - rate = rate_lookup[i].rate; - freq = rate_lookup[i].freq; - break; - } - } - s->rateadc = rate; - freq <<= CM_FREQ_ADCSHIFT; - - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~ASFC, freq); -} - -static void set_adc_rate(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - unsigned char freq = 4; - int i; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { - rate = rate_lookup[i].rate; - freq = rate_lookup[i].freq; - break; - } - } - s->rateadc = rate; - freq <<= CM_FREQ_ADCSHIFT; - - spin_lock_irqsave(&s->lock, flags); - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~ASFC, freq); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void set_dac_rate(struct cm_state *s, unsigned rate) -{ - unsigned long flags; - unsigned char freq = 4; - int i; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { - rate = rate_lookup[i].rate; - freq = rate_lookup[i].freq; - break; - } - } - s->ratedac = rate; - freq <<= CM_FREQ_DACSHIFT; - - spin_lock_irqsave(&s->lock, flags); - maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~DSFC, freq); - spin_unlock_irqrestore(&s->lock, flags); - - if (s->curr_channels <= 2 && spdif_out) - set_spdifout(s, rate); - if (s->status & DO_DUAL_DAC) - set_dac1_rate(s, rate); -} - -/* --------------------------------------------------------------------- */ -static inline void reset_adc(struct cm_state *s) -{ - /* reset bus master */ - outb(s->enable | RSTADC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - udelay(10); - outb(s->enable & ~RSTADC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); -} - -static inline void reset_dac(struct cm_state *s) -{ - /* reset bus master */ - outb(s->enable | RSTDAC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - udelay(10); - outb(s->enable & ~RSTDAC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - if (s->status & DO_DUAL_DAC) - reset_adc(s); -} - -static inline void pause_adc(struct cm_state *s) -{ - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, PAUSEADC); -} - -static inline void pause_dac(struct cm_state *s) -{ - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, PAUSEDAC); - if (s->status & DO_DUAL_DAC) - pause_adc(s); -} - -static inline void disable_adc(struct cm_state *s) -{ - /* disable channel */ - s->enable &= ~ENADC; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - reset_adc(s); -} - -static inline void disable_dac(struct cm_state *s) -{ - /* disable channel */ - s->enable &= ~ENDAC; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - reset_dac(s); - if (s->status & DO_DUAL_DAC) - disable_adc(s); -} - -static inline void enable_adc(struct cm_state *s) -{ - if (!(s->enable & ENADC)) { - /* enable channel */ - s->enable |= ENADC; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - } - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~PAUSEADC, 0); -} - -static inline void enable_dac_unlocked(struct cm_state *s) -{ - if (!(s->enable & ENDAC)) { - /* enable channel */ - s->enable |= ENDAC; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - } - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~PAUSEDAC, 0); - - if (s->status & DO_DUAL_DAC) - enable_adc(s); -} - -static inline void stop_adc_unlocked(struct cm_state *s) -{ - if (s->enable & ENADC) { - /* disable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~ENADCINT, 0); - disable_adc(s); - } -} - -static inline void stop_adc(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - stop_adc_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); - -} - -static inline void stop_dac_unlocked(struct cm_state *s) -{ - if (s->enable & ENDAC) { - /* disable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~ENDACINT, 0); - disable_dac(s); - } - if (s->status & DO_DUAL_DAC) - stop_dac1_unlocked(s); -} - -static inline void stop_dac(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - stop_dac_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void start_adc_unlocked(struct cm_state *s) -{ - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - /* enable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, ENADCINT); - enable_adc(s); - } -} - -static void start_adc(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - start_adc_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac1_unlocked(struct cm_state *s) -{ - if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { - /* enable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, ENADCINT); - enable_dac_unlocked(s); - } -} - -static void start_dac_unlocked(struct cm_state *s) -{ - if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { - /* enable interrupt */ - maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, ENDACINT); - enable_dac_unlocked(s); - } - if (s->status & DO_DUAL_DAC) - start_dac1_unlocked(s); -} - -static void start_dac(struct cm_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - start_dac_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); -} - -static int prog_dmabuf(struct cm_state *s, unsigned rec); - -static int set_dac_channels(struct cm_state *s, int channels) -{ - unsigned long flags; - static unsigned int fmmute = 0; - - spin_lock_irqsave(&s->lock, flags); - - if ((channels > 2) && (channels <= s->max_channels) - && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { - set_spdifout_unlocked(s, 0); - if (s->capability & CAN_MULTI_CH_HW) { - // NXCHG - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, NXCHG); - // CHB3D or CHB3D5C - maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~(CHB3D5C|CHB3D), channels > 4 ? CHB3D5C : CHB3D); - // CHB3D6C - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CHB3D6C, channels == 6 ? CHB3D6C : 0); - // ENCENTER - maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~ENCENTER, channels == 6 ? ENCENTER : 0); - s->status |= DO_MULTI_CH_HW; - } else if (s->capability & CAN_DUAL_DAC) { - unsigned char fmtm = ~0, fmts = 0; - ssize_t ret; - - // ENDBDAC, turn on double DAC mode - // XCHGDAC, CH0 -> back, CH1->front - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, ENDBDAC|XCHGDAC); - // mute FM - fmmute = inb(s->iobase + CODEC_CMI_MIXER1) & FMMUTE; - maskb(s->iobase + CODEC_CMI_MIXER1, ~0, FMMUTE); - s->status |= DO_DUAL_DAC; - // prepare secondary buffer - spin_unlock_irqrestore(&s->lock, flags); - ret = prog_dmabuf(s, 1); - if (ret) return ret; - spin_lock_irqsave(&s->lock, flags); - - // copy the hw state - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); - // the HW only support 16-bit stereo - fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; - fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; - fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - - set_fmt_unlocked(s, fmtm, fmts); - set_adc_rate_unlocked(s, s->ratedac); - } - // disable 4 speaker mode (analog duplicate) - set_hw_copy(s, 0); - s->curr_channels = channels; - - // enable jack redirect - set_line_as_rear(s, use_line_as_rear); - if (channels > 4) { - set_line_as_bass(s, use_line_as_bass); - set_mic_as_bass(s, use_mic_as_bass); - } - } else { - if (s->status & DO_MULTI_CH_HW) { - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~NXCHG, 0); - maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~(CHB3D5C|CHB3D), 0); - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CHB3D6C, 0); - } else if (s->status & DO_DUAL_DAC) { - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~ENDBDAC, 0); - maskb(s->iobase + CODEC_CMI_MIXER1, ~FMMUTE, fmmute); - } - // enable 4 speaker mode (analog duplicate) - set_hw_copy(s, hw_copy); - s->status &= ~DO_MULTI_CH; - s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; - // disable jack redirect - set_line_as_rear(s, hw_copy ? use_line_as_rear : 0); - set_line_as_bass(s, 0); - set_mic_as_bass(s, 0); - } - spin_unlock_irqrestore(&s->lock, flags); - return s->curr_channels; -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static void dealloc_dmabuf(struct cm_state *s, struct dmabuf *db) -{ - struct page *pstart, *pend; - - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) - ClearPageReserved(pstart); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -/* Ch1 is used for playback, Ch0 is used for recording */ - -static int prog_dmabuf(struct cm_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - int order; - unsigned bytepersec; - unsigned bufs; - struct page *pstart, *pend; - unsigned char fmt; - unsigned long flags; - - fmt = s->fmt; - if (rec) { - stop_adc(s); - fmt >>= CM_CFMT_ADCSHIFT; - } else { - stop_dac(s); - fmt >>= CM_CFMT_DACSHIFT; - } - - fmt &= CM_CFMT_MASK; - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) - break; - if (!db->rawbuf || !db->dmaaddr) - return -ENOMEM; - db->buforder = order; - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) - SetPageReserved(pstart); - } - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - /* to make fragsize >= 4096 */ - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - db->dmasamples = db->dmasize >> sample_shift[fmt]; - memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); - spin_lock_irqsave(&s->lock, flags); - if (rec) { - if (s->status & DO_DUAL_DAC) - set_dmadac1(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); - else - set_dmaadc(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); - /* program sample counts */ - set_countdac(s, db->fragsamples); - } else { - set_dmadac(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); - /* program sample counts */ - set_countdac(s, db->fragsamples); - } - spin_unlock_irqrestore(&s->lock, flags); - db->enabled = 1; - db->ready = 1; - return 0; -} - -static inline void clear_advance(struct cm_state *s) -{ - unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; - unsigned char *buf = s->dma_dac.rawbuf; - unsigned char *buf1 = s->dma_adc.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - if (s->status & DO_DUAL_DAC) - memset(buf1 + bptr, c, x); - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); - if (s->status & DO_DUAL_DAC) - memset(buf1 + bptr, c, len); -} - -/* call with spinlock held! */ -static void cm_update_ptr(struct cm_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - if (s->status & DO_DUAL_DAC) { - /* the dac part will finish for this */ - } else { - hwptr = get_dmaadc(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - pause_adc(s); - s->dma_adc.error++; - } - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmadac(s) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->status & DO_DUAL_DAC) { - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - } - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->status & DO_DUAL_DAC) - s->dma_adc.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } else { - s->dma_dac.count -= diff; - if (s->status & DO_DUAL_DAC) - s->dma_adc.count -= diff; - if (s->dma_dac.count <= 0) { - pause_dac(s); - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - if (s->status & DO_DUAL_DAC) - s->dma_adc.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); - } - } -} - -static irqreturn_t cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cm_state *s = (struct cm_state *)dev_id; - unsigned int intsrc, intstat; - unsigned char mask = 0; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); - if (!(intsrc & 0x80000000)) - return IRQ_NONE; - spin_lock(&s->lock); - intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* acknowledge interrupt */ - if (intsrc & ADCINT) - mask |= ENADCINT; - if (intsrc & DACINT) - mask |= ENDACINT; - outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - cm_update_ptr(s); - spin_unlock(&s->lock); -#ifdef CONFIG_SOUND_CMPCI_MIDI - if (intsrc & 0x00010000) { // UART interrupt - if (s->midi_devc && intchk_mpu401((void *)s->midi_devc)) - mpuintr(irq, (void *)s->midi_devc, regs); - else - inb(s->iomidi);// dummy read - } -#endif - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != CM_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -#define MT_4 1 -#define MT_5MUTE 2 -#define MT_4MUTEMONO 3 -#define MT_6MUTE 4 -#define MT_5MUTEMONO 5 - -static const struct { - unsigned left; - unsigned right; - unsigned type; - unsigned rec; - unsigned play; -} mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x06 }, - [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x18 }, - [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, - [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, - [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, - [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, - [SOUND_MIXER_LINE1] = { DSP_MIX_AUXVOL_L, DSP_MIX_AUXVOL_R, MT_5MUTE, 0x80, 0x60 }, - [SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX, DSP_MIX_SPKRVOLIDX, MT_5MUTEMONO, 0x00, 0x01 } -}; - -static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = -{ - [SOUND_MIXER_CD] = 1, - [SOUND_MIXER_LINE] = 2, - [SOUND_MIXER_MIC] = 3, - [SOUND_MIXER_SYNTH] = 4, - [SOUND_MIXER_VOLUME] = 5, - [SOUND_MIXER_PCM] = 6, - [SOUND_MIXER_LINE1] = 7, - [SOUND_MIXER_SPEAKER]= 8 -}; - -static unsigned mixer_outmask(struct cm_state *s) -{ - unsigned long flags; - int i, j, k; - - spin_lock_irqsave(&s->lock, flags); - j = rdmixer(s, DSP_MIX_OUTMIXIDX); - spin_unlock_irqrestore(&s->lock, flags); - for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (j & mixtable[i].play) - k |= 1 << i; - return k; -} - -static unsigned mixer_recmask(struct cm_state *s) -{ - unsigned long flags; - int i, j, k; - - spin_lock_irqsave(&s->lock, flags); - j = rdmixer(s, DSP_MIX_ADCMIXIDX_L); - spin_unlock_irqrestore(&s->lock, flags); - for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (j & mixtable[i].rec) - k |= 1 << i; - return k; -} - -static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val, j; - unsigned char l, r, rl, rr; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, "cmpci", sizeof(info.id)); - strlcpy(info.name, "C-Media PCI", sizeof(info.name)); - info.modify_counter = s->mix.modcnt; - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, "cmpci", sizeof(info.id)); - strlcpy(info.name, "C-Media cmpci", sizeof(info.name)); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - val = mixer_recmask(s); - return put_user(val, p); - - case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ - val = mixer_outmask(s); - return put_user(val, p); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].rec) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].play) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_CAPS: - return put_user(0, p); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; - if (!volidx[i]) - return -EINVAL; - return put_user(s->mix.vol[volidx[i]-1], p); - } - } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, p)) - return -EFAULT; - i = hweight32(val); - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].rec) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].rec; - } - spin_lock_irqsave(&s->lock, flags); - wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); - wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1) | (j & 0x80)); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, p)) - return -EFAULT; - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].play) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].play; - } - spin_lock_irqsave(&s->lock, flags); - wrmixer(s, DSP_MIX_OUTMIXIDX, j); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - l = val & 0xff; - r = (val >> 8) & 0xff; - if (l > 100) - l = 100; - if (r > 100) - r = 100; - spin_lock_irqsave(&s->lock, flags); - switch (mixtable[i].type) { - case MT_4: - if (l >= 10) - l -= 10; - if (r >= 10) - r -= 10; - frobindir(s, mixtable[i].left, 0xf0, l / 6); - frobindir(s, mixtable[i].right, 0xf0, l / 6); - break; - - case MT_4MUTEMONO: - rl = (l < 4 ? 0 : (l - 5) / 3) & 31; - rr = (rl >> 2) & 7; - wrmixer(s, mixtable[i].left, rl<<3); - if (i == SOUND_MIXER_MIC) - maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); - break; - - case MT_5MUTEMONO: - rl = l < 4 ? 0 : (l - 5) / 3; - wrmixer(s, mixtable[i].left, rl<<3); - l = rdmixer(s, DSP_MIX_OUTMIXIDX) & ~mixtable[i].play; - r = rl ? mixtable[i].play : 0; - wrmixer(s, DSP_MIX_OUTMIXIDX, l | r); - /* for recording */ - if (i == SOUND_MIXER_MIC) { - if (s->chip_version >= 37) { - rr = rl >> 1; - maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, (rr&0x07)<<1); - frobindir(s, DSP_MIX_EXTENSION, ~0x01, rr>>3); - } else { - rr = rl >> 2; - maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); - } - } - break; - - case MT_5MUTE: - rl = l < 4 ? 0 : (l - 5) / 3; - rr = r < 4 ? 0 : (r - 5) / 3; - wrmixer(s, mixtable[i].left, rl<<3); - wrmixer(s, mixtable[i].right, rr<<3); - l = rdmixer(s, DSP_MIX_OUTMIXIDX); - l &= ~mixtable[i].play; - r = (rl|rr) ? mixtable[i].play : 0; - wrmixer(s, DSP_MIX_OUTMIXIDX, l | r); - break; - - case MT_6MUTE: - if (l < 6) - rl = 0x00; - else - rl = l * 2 / 3; - if (r < 6) - rr = 0x00; - else - rr = r * 2 / 3; - wrmixer(s, mixtable[i].left, rl); - wrmixer(s, mixtable[i].right, rr); - break; - } - spin_unlock_irqrestore(&s->lock, flags); - - if (!volidx[i]) - return -EINVAL; - s->mix.vol[volidx[i]-1] = val; - return put_user(s->mix.vol[volidx[i]-1], p); - } -} - -/* --------------------------------------------------------------------- */ - -static int cm_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct list_head *list; - struct cm_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct cm_state, devs); - if (s->dev_mixer == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - return nonseekable_open(inode, file); -} - -static int cm_release_mixdev(struct inode *inode, struct file *file) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations cm_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = cm_ioctl_mixdev, - .open = cm_open_mixdev, - .release = cm_release_mixdev, -}; - - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct cm_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; - tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG "cmpci: dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t cm_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto out; - } - if (!schedule_timeout(HZ)) { - printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - spin_lock_irqsave(&s->lock, flags); - stop_adc_unlocked(s); - set_dmaadc(s, s->dma_adc.dmaaddr, s->dma_adc.dmasamples); - /* program sample counts */ - set_countadc(s, s->dma_adc.fragsamples); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto out; - } - continue; - } - if (s->status & DO_BIGENDIAN_R) { - int i, err; - unsigned char *src; - char __user *dst = buffer; - unsigned char data[2]; - - src = (unsigned char *) (s->dma_adc.rawbuf + swptr); - // copy left/right sample at one time - for (i = 0; i < cnt / 2; i++) { - data[0] = src[1]; - data[1] = src[0]; - if ((err = __put_user(data[0], dst++))) { - ret = err; - goto out; - } - if ((err = __put_user(data[1], dst++))) { - ret = err; - goto out; - } - src += 2; - } - } else if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc_unlocked(s); - spin_unlock_irqrestore(&s->lock, flags); - } -out: - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t cm_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (s->status & DO_DUAL_DAC) { - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - } - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - add_wait_queue(&s->dma_dac.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - if (s->status & DO_DUAL_DAC) { - s->dma_adc.swptr = s->dma_dac.swptr; - s->dma_adc.count = s->dma_dac.count; - s->dma_adc.endcleared = s->dma_dac.endcleared; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize-swptr; - if (s->status & DO_AC3_SW) { - if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize) - cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2; - } else { - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - } - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if ((s->status & DO_DUAL_DAC) && (cnt > count / 2)) - cnt = count / 2; - if (cnt <= 0) { - if (s->dma_dac.enabled) - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto out; - } - if (!schedule_timeout(HZ)) { - printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - spin_lock_irqsave(&s->lock, flags); - stop_dac_unlocked(s); - set_dmadac(s, s->dma_dac.dmaaddr, s->dma_dac.dmasamples); - /* program sample counts */ - set_countdac(s, s->dma_dac.fragsamples); - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - if (s->status & DO_DUAL_DAC) { - set_dmadac1(s, s->dma_adc.dmaaddr, s->dma_adc.dmasamples); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto out; - } - continue; - } - if (s->status & DO_AC3_SW) { - int err; - - // clip exceeded data, caught by 033 and 037 - if (swptr + 2 * cnt > s->dma_dac.dmasize) - cnt = (s->dma_dac.dmasize - swptr) / 2; - if ((err = trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt))) { - ret = err; - goto out; - } - swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize; - } else if ((s->status & DO_DUAL_DAC) && (s->status & DO_BIGENDIAN_W)) { - int i, err; - const char __user *src = buffer; - unsigned char *dst0, *dst1; - unsigned char data[8]; - - dst0 = (unsigned char *) (s->dma_dac.rawbuf + swptr); - dst1 = (unsigned char *) (s->dma_adc.rawbuf + swptr); - // copy left/right sample at one time - for (i = 0; i < cnt / 4; i++) { - if ((err = __get_user(data[0], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[1], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[2], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[3], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[4], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[5], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[6], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[7], src++))) { - ret = err; - goto out; - } - dst0[0] = data[1]; - dst0[1] = data[0]; - dst0[2] = data[3]; - dst0[3] = data[2]; - dst1[0] = data[5]; - dst1[1] = data[4]; - dst1[2] = data[7]; - dst1[3] = data[6]; - dst0 += 4; - dst1 += 4; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - } else if (s->status & DO_DUAL_DAC) { - int i, err; - unsigned long __user *src = (unsigned long __user *) buffer; - unsigned long *dst0, *dst1; - - dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr); - dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr); - // copy left/right sample at one time - for (i = 0; i < cnt / 4; i++) { - if ((err = __get_user(*dst0++, src++))) { - ret = err; - goto out; - } - if ((err = __get_user(*dst1++, src++))) { - ret = err; - goto out; - } - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - } else if (s->status & DO_BIGENDIAN_W) { - int i, err; - const char __user *src = buffer; - unsigned char *dst; - unsigned char data[2]; - - dst = (unsigned char *) (s->dma_dac.rawbuf + swptr); - // swap hi/lo bytes for each sample - for (i = 0; i < cnt / 2; i++) { - if ((err = __get_user(data[0], src++))) { - ret = err; - goto out; - } - if ((err = __get_user(data[1], src++))) { - ret = err; - goto out; - } - dst[0] = data[1]; - dst[1] = data[0]; - dst += 2; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - } else { - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - } - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - if (s->status & DO_AC3_SW) - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->status & DO_DUAL_DAC) { - count -= cnt; - buffer += cnt; - ret += cnt; - } - if (s->dma_dac.enabled) - start_dac(s); - } -out: - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf(s, 0)) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf(s, 1)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int cm_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 0)) != 0) - goto out; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 1)) != 0) - goto out; - db = &s->dma_adc; - } else - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EINVAL; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -#define SNDCTL_SPDIF_COPYRIGHT _SIOW('S', 0, int) // set/reset S/PDIF copy protection -#define SNDCTL_SPDIF_LOOP _SIOW('S', 1, int) // set/reset S/PDIF loop -#define SNDCTL_SPDIF_MONITOR _SIOW('S', 2, int) // set S/PDIF monitor -#define SNDCTL_SPDIF_LEVEL _SIOW('S', 3, int) // set/reset S/PDIF out level -#define SNDCTL_SPDIF_INV _SIOW('S', 4, int) // set/reset S/PDIF in inverse -#define SNDCTL_SPDIF_SEL2 _SIOW('S', 5, int) // set S/PDIF in #2 -#define SNDCTL_SPDIF_VALID _SIOW('S', 6, int) // set S/PDIF valid -#define SNDCTL_SPDIFOUT _SIOW('S', 7, int) // set S/PDIF out -#define SNDCTL_SPDIFIN _SIOW('S', 8, int) // set S/PDIF out - -static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - unsigned char fmtm, fmtd; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_BIND, p); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - if (s->status & DO_DUAL_DAC) - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - spin_lock_irqsave(&s->lock, flags); - stop_adc_unlocked(s); - s->dma_adc.ready = 0; - set_adc_rate_unlocked(s, val); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (s->status & DO_DUAL_DAC) - s->dma_adc.ready = 0; - set_dac_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ready = 0; - if (val) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - } - set_fmt(s, fmtm, fmtd); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - } - set_fmt(s, fmtm, fmtd); - if ((s->capability & CAN_MULTI_CH) - && (file->f_mode & FMODE_WRITE)) { - val = set_dac_channels(s, val); - return put_user(val, p); - } - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) - : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_BE|AFMT_S16_LE|AFMT_U8| - ((s->capability & CAN_AC3) ? AFMT_AC3 : 0), p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val == AFMT_S16_BE || val == AFMT_S16_LE) - fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT); - if (val == AFMT_S16_BE) - s->status |= DO_BIGENDIAN_R; - else - s->status &= ~DO_BIGENDIAN_R; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_BE || val == AFMT_S16_LE || val == AFMT_AC3) - fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; - else - fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT); - if (val == AFMT_AC3) { - fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; - set_ac3(s, 48000); - } else - set_ac3(s, 0); - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ready = 0; - if (val == AFMT_S16_BE || val == AFMT_S16_LE) - fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; - else - fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); - } - if (val == AFMT_S16_BE) - s->status |= DO_BIGENDIAN_W; - else - s->status &= ~DO_BIGENDIAN_W; - } - set_fmt(s, fmtm, fmtd); - } - if (s->status & DO_AC3) return put_user(AFMT_AC3, p); - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) - : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? val : AFMT_U8, p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (s->status & DO_DUAL_DAC) { - if (file->f_mode & FMODE_WRITE && - (s->enable & ENDAC) && - (s->enable & ENADC)) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - } - if (file->f_mode & FMODE_READ && s->enable & ENADC) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & ENDAC) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - s->dma_adc.enabled = 1; - start_adc(s); - } else { - s->dma_adc.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - if (s->status & DO_DUAL_DAC) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - } - s->dma_dac.enabled = 1; - start_dac(s); - } else { - s->dma_dac.enabled = 0; - stop_dac(s); - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!(s->enable & ENDAC) && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!(s->enable & ENADC) && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - val = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->lock, flags); - cm_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - if (s->status & DO_DUAL_DAC) { - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - } - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) - return val; - if (s->status & DO_DUAL_DAC) { - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(2 * s->dma_dac.fragsize, p); - } - return put_user(s->dma_dac.fragsize, p); - } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - if (s->status & DO_DUAL_DAC) { - s->dma_adc.ossfragshift = s->dma_dac.ossfragshift; - s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags; - } - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.subdivision = val; - if (s->status & DO_DUAL_DAC) - s->dma_adc.subdivision = val; - } - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, p); - - case SOUND_PCM_READ_FILTER: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SNDCTL_DSP_GETCHANNELMASK: - return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, p); - - case SNDCTL_DSP_BIND_CHANNEL: - if (get_user(val, p)) - return -EFAULT; - if (val == DSP_BIND_QUERY) { - val = DSP_BIND_FRONT; - if (s->status & DO_SPDIF_OUT) - val |= DSP_BIND_SPDIF; - else { - if (s->curr_channels == 4) - val |= DSP_BIND_SURR; - if (s->curr_channels > 4) - val |= DSP_BIND_CENTER_LFE; - } - } else { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val & DSP_BIND_SPDIF) { - set_spdifin(s, s->rateadc); - if (!(s->status & DO_SPDIF_OUT)) - val &= ~DSP_BIND_SPDIF; - } - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val & DSP_BIND_SPDIF) { - set_spdifout(s, s->ratedac); - set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1); - if (!(s->status & DO_SPDIF_OUT)) - val &= ~DSP_BIND_SPDIF; - } else { - int channels; - int mask; - - mask = val & (DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE); - switch (mask) { - case DSP_BIND_FRONT: - channels = 2; - break; - case DSP_BIND_FRONT|DSP_BIND_SURR: - channels = 4; - break; - case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE: - channels = 6; - break; - default: - channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; - break; - } - set_dac_channels(s, channels); - } - } - } - return put_user(val, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - case SNDCTL_DSP_SETSYNCRO: - return -EINVAL; - case SNDCTL_SPDIF_COPYRIGHT: - if (get_user(val, p)) - return -EFAULT; - set_spdif_copyright(s, val); - return 0; - case SNDCTL_SPDIF_LOOP: - if (get_user(val, p)) - return -EFAULT; - set_spdif_loop(s, val); - return 0; - case SNDCTL_SPDIF_MONITOR: - if (get_user(val, p)) - return -EFAULT; - set_spdif_monitor(s, val); - return 0; - case SNDCTL_SPDIF_LEVEL: - if (get_user(val, p)) - return -EFAULT; - set_spdifout_level(s, val); - return 0; - case SNDCTL_SPDIF_INV: - if (get_user(val, p)) - return -EFAULT; - set_spdifin_inverse(s, val); - return 0; - case SNDCTL_SPDIF_SEL2: - if (get_user(val, p)) - return -EFAULT; - set_spdifin_channel2(s, val); - return 0; - case SNDCTL_SPDIF_VALID: - if (get_user(val, p)) - return -EFAULT; - set_spdifin_valid(s, val); - return 0; - case SNDCTL_SPDIFOUT: - if (get_user(val, p)) - return -EFAULT; - set_spdifout(s, val ? s->ratedac : 0); - return 0; - case SNDCTL_SPDIFIN: - if (get_user(val, p)) - return -EFAULT; - set_spdifin(s, val ? s->rateadc : 0); - return 0; - } - return mixer_ioctl(s, cmd, arg); -} - -static int cm_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned char fmtm = ~0, fmts = 0; - struct list_head *list; - struct cm_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct cm_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - if (file->f_mode & FMODE_READ) { - s->status &= ~DO_BIGENDIAN_R; - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - set_adc_rate(s, 8000); - // spdif-in is turnned off by default - set_spdifin(s, 0); - } - if (file->f_mode & FMODE_WRITE) { - s->status &= ~DO_BIGENDIAN_W; - fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - s->dma_dac.enabled = 1; - set_dac_rate(s, 8000); - // clear previous multichannel, spdif, ac3 state - set_spdifout(s, 0); - set_ac3(s, 0); - set_dac_channels(s, 1); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int cm_release(struct inode *inode, struct file *file) -{ - struct cm_state *s = (struct cm_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_mutex); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - - dealloc_dmabuf(s, &s->dma_dac); - if (s->status & DO_DUAL_DAC) - dealloc_dmabuf(s, &s->dma_adc); - - if (s->status & DO_MULTI_CH) - set_dac_channels(s, 1); - if (s->status & DO_AC3) - set_ac3(s, 0); - if (s->status & DO_SPDIF_OUT) - set_spdifout(s, 0); - /* enable SPDIF loop */ - set_spdif_loop(s, spdif_loop); - s->status &= ~DO_BIGENDIAN_W; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - s->status &= ~DO_BIGENDIAN_R; - } - s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); - mutex_unlock(&s->open_mutex); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations cm_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = cm_read, - .write = cm_write, - .poll = cm_poll, - .ioctl = cm_ioctl, - .mmap = cm_mmap, - .open = cm_open, - .release = cm_release, -}; - -/* --------------------------------------------------------------------- */ - -static struct initvol { - int mixch; - int vol; -} initvol[] __devinitdata = { - { SOUND_MIXER_WRITE_CD, 0x4f4f }, - { SOUND_MIXER_WRITE_LINE, 0x4f4f }, - { SOUND_MIXER_WRITE_MIC, 0x4f4f }, - { SOUND_MIXER_WRITE_SYNTH, 0x4f4f }, - { SOUND_MIXER_WRITE_VOLUME, 0x4f4f }, - { SOUND_MIXER_WRITE_PCM, 0x4f4f } -}; - -/* check chip version and capability */ -static int query_chip(struct cm_state *s) -{ - int ChipVersion = -1; - unsigned char RegValue; - - // check reg 0Ch, bit 24-31 - RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3); - if (RegValue == 0) { - // check reg 08h, bit 24-28 - RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3); - RegValue &= 0x1f; - if (RegValue == 0) { - ChipVersion = 33; - s->max_channels = 4; - s->capability |= CAN_AC3_SW; - s->capability |= CAN_DUAL_DAC; - } else { - ChipVersion = 37; - s->max_channels = 4; - s->capability |= CAN_AC3_HW; - s->capability |= CAN_DUAL_DAC; - } - } else { - // check reg 0Ch, bit 26 - if (RegValue & (1 << (26-24))) { - ChipVersion = 39; - if (RegValue & (1 << (24-24))) - s->max_channels = 6; - else - s->max_channels = 4; - s->capability |= CAN_AC3_HW; - s->capability |= CAN_DUAL_DAC; - s->capability |= CAN_MULTI_CH_HW; - s->capability |= CAN_LINE_AS_BASS; - s->capability |= CAN_MIC_AS_BASS; - } else { - ChipVersion = 55; // 4 or 6 channels - s->max_channels = 6; - s->capability |= CAN_AC3_HW; - s->capability |= CAN_DUAL_DAC; - s->capability |= CAN_MULTI_CH_HW; - s->capability |= CAN_LINE_AS_BASS; - s->capability |= CAN_MIC_AS_BASS; - } - } - s->capability |= CAN_LINE_AS_REAR; - return ChipVersion; -} - -#ifdef CONFIG_SOUND_CMPCI_JOYSTICK -static int __devinit cm_create_gameport(struct cm_state *s, int io_port) -{ - struct gameport *gp; - - if (!request_region(io_port, CM_EXTENT_GAME, "cmpci GAME")) { - printk(KERN_ERR "cmpci: gameport io ports 0x%#x in use\n", io_port); - return -EBUSY; - } - - if (!(s->gameport = gp = gameport_allocate_port())) { - printk(KERN_ERR "cmpci: can not allocate memory for gameport\n"); - release_region(io_port, CM_EXTENT_GAME); - return -ENOMEM; - } - - gameport_set_name(gp, "C-Media GP"); - gameport_set_phys(gp, "pci%s/gameport0", pci_name(s->dev)); - gp->dev.parent = &s->dev->dev; - gp->io = io_port; - - /* enable joystick */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02); - - gameport_register_port(gp); - - return 0; -} - -static void __devexit cm_free_gameport(struct cm_state *s) -{ - if (s->gameport) { - int gpio = s->gameport->io; - - gameport_unregister_port(s->gameport); - s->gameport = NULL; - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); - release_region(gpio, CM_EXTENT_GAME); - } -} -#else -static inline int cm_create_gameport(struct cm_state *s, int io_port) { return -ENOSYS; } -static inline void cm_free_gameport(struct cm_state *s) { } -#endif - -#define echo_option(x)\ -if (x) strcat(options, "" #x " ") - -static int __devinit cm_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - struct cm_state *s; - mm_segment_t fs; - int i, val, ret; - unsigned char reg_mask; - int timeout; - struct resource *ports; - struct { - unsigned short deviceid; - char *devicename; - } devicetable[] = { - { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" }, - { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" }, - { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" }, - { PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" }, - }; - char *devicename = "unknown"; - char options[256]; - - if ((ret = pci_enable_device(pcidev))) - return ret; - if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO)) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - i = pci_set_dma_mask(pcidev, DMA_32BIT_MASK); - if (i) { - printk(KERN_WARNING "cmpci: architecture does not support 32bit PCI busmaster DMA\n"); - return i; - } - s = kmalloc(sizeof(*s), GFP_KERNEL); - if (!s) { - printk(KERN_WARNING "cmpci: out of memory\n"); - return -ENOMEM; - } - /* search device name */ - for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) { - if (devicetable[i].deviceid == pcidev->device) { - devicename = devicetable[i].devicename; - break; - } - } - memset(s, 0, sizeof(struct cm_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - mutex_init(&s->open_mutex); - spin_lock_init(&s->lock); - s->magic = CM_MAGIC; - s->dev = pcidev; - s->iobase = pci_resource_start(pcidev, 0); - s->iosynth = fmio; - s->iomidi = mpuio; -#ifdef CONFIG_SOUND_CMPCI_MIDI - s->midi_devc = 0; -#endif - s->status = 0; - if (s->iobase == 0) - return -ENODEV; - s->irq = pcidev->irq; - - if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) { - printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); - ret = -EBUSY; - goto err_region5; - } - /* dump parameters */ - strcpy(options, "cmpci: "); - echo_option(joystick); - echo_option(spdif_inverse); - echo_option(spdif_loop); - echo_option(spdif_out); - echo_option(use_line_as_rear); - echo_option(use_line_as_bass); - echo_option(use_mic_as_bass); - echo_option(mic_boost); - echo_option(hw_copy); - printk(KERN_INFO "%s\n", options); - - /* initialize codec registers */ - outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ - outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ - /* reset mixer */ - wrmixer(s, DSP_MIX_DATARESETIDX, 0); - - /* request irq */ - if ((ret = request_irq(s->irq, cm_interrupt, IRQF_SHARED, "cmpci", s))) { - printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO "cmpci: found %s adapter at io %#x irq %u\n", - devicename, s->iobase, s->irq); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - pci_set_master(pcidev); /* enable bus mastering */ - /* initialize the chips */ - fs = get_fs(); - set_fs(KERNEL_DS); - /* set mixer output */ - frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f); - /* set mixer input */ - val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); - } - set_fs(fs); - /* use channel 1 for playback, channel 0 for record */ - maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC1, CHADC0); - /* turn off VMIC3 - mic boost */ - if (mic_boost) - maskb(s->iobase + CODEC_CMI_MIXER2, ~1, 0); - else - maskb(s->iobase + CODEC_CMI_MIXER2, ~0, 1); - s->deviceid = pcidev->device; - - if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738 - || pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { - - /* chip version and hw capability check */ - s->chip_version = query_chip(s); - printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version); - - /* set SPDIF-in inverse before enable SPDIF loop */ - set_spdifin_inverse(s, spdif_inverse); - - /* use SPDIF in #1 */ - set_spdifin_channel2(s, 0); - } else { - s->chip_version = 0; - /* 8338 will fall here */ - s->max_channels = 4; - s->capability |= CAN_DUAL_DAC; - s->capability |= CAN_LINE_AS_REAR; - } - /* enable SPDIF loop */ - set_spdif_loop(s, spdif_loop); - - // enable 4 speaker mode (analog duplicate) - set_hw_copy(s, hw_copy); - - reg_mask = 0; -#ifdef CONFIG_SOUND_CMPCI_FM - /* disable FM */ - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); - if (s->iosynth) { - /* don't enable OPL3 if there is one */ - if (opl3_detect(s->iosynth, NULL)) { - s->iosynth = 0; - } else { - /* set IO based at 0x388 */ - switch (s->iosynth) { - case 0x388: - reg_mask = 0; - break; - case 0x3C8: - reg_mask = 0x01; - break; - case 0x3E0: - reg_mask = 0x02; - break; - case 0x3E8: - reg_mask = 0x03; - break; - default: - s->iosynth = 0; - break; - } - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask); - /* enable FM */ - if (s->iosynth) { - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8); - if (opl3_detect(s->iosynth, NULL)) - ret = opl3_init(s->iosynth, NULL, THIS_MODULE); - else { - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); - s->iosynth = 0; - } - } - } - } -#endif -#ifdef CONFIG_SOUND_CMPCI_MIDI - switch (s->iomidi) { - case 0x330: - reg_mask = 0; - break; - case 0x320: - reg_mask = 0x20; - break; - case 0x310: - reg_mask = 0x40; - break; - case 0x300: - reg_mask = 0x60; - break; - default: - s->iomidi = 0; - goto skip_mpu; - } - ports = request_region(s->iomidi, 2, "mpu401"); - if (!ports) - goto skip_mpu; - /* disable MPU-401 */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); - s->mpu_data.name = "cmpci mpu"; - s->mpu_data.io_base = s->iomidi; - s->mpu_data.irq = -s->irq; // tell mpu401 to share irq - if (probe_mpu401(&s->mpu_data, ports)) { - release_region(s->iomidi, 2); - s->iomidi = 0; - goto skip_mpu; - } - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x60, reg_mask); - /* enable MPU-401 */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); - /* clear all previously received interrupt */ - for (timeout = 900000; timeout > 0; timeout--) { - if ((inb(s->iomidi + 1) && 0x80) == 0) - inb(s->iomidi); - else - break; - } - if (!probe_mpu401(&s->mpu_data, ports)) { - release_region(s->iomidi, 2); - s->iomidi = 0; - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); - } else { - attach_mpu401(&s->mpu_data, THIS_MODULE); - s->midi_devc = s->mpu_data.slots[1]; - } -skip_mpu: -#endif - /* disable joystick port */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); - if (joystick) - cm_create_gameport(s, 0x200); - - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; - -err_dev2: - unregister_sound_dsp(s->dev_audio); -err_dev1: - printk(KERN_ERR "cmpci: cannot register misc device\n"); - free_irq(s->irq, s); -err_irq: - release_region(s->iobase, CM_EXTENT_CODEC); -err_region5: - kfree(s); - return ret; -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw"); -MODULE_DESCRIPTION("CM8x38 Audio Driver"); -MODULE_LICENSE("GPL"); - -static void __devexit cm_remove(struct pci_dev *dev) -{ - struct cm_state *s = pci_get_drvdata(dev); - - if (!s) - return; - - cm_free_gameport(s); - -#ifdef CONFIG_SOUND_CMPCI_FM - if (s->iosynth) { - /* disable FM */ - maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); - } -#endif -#ifdef CONFIG_SOUND_CMPCI_MIDI - if (s->iomidi) { - unload_mpu401(&s->mpu_data); - /* disable MPU-401 */ - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); - } -#endif - set_spdif_loop(s, 0); - list_del(&s->devs); - outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ - synchronize_irq(s->irq); - outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ - free_irq(s->irq, s); - - /* reset mixer */ - wrmixer(s, DSP_MIX_DATARESETIDX, 0); - - release_region(s->iobase, CM_EXTENT_CODEC); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] __devinitdata = { - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver cm_driver = { - .name = "cmpci", - .id_table = id_table, - .probe = cm_probe, - .remove = __devexit_p(cm_remove) -}; - -static int __init init_cmpci(void) -{ - printk(KERN_INFO "cmpci: version $Revision: 6.82 $ time " __TIME__ " " __DATE__ "\n"); - return pci_register_driver(&cm_driver); -} - -static void __exit cleanup_cmpci(void) -{ - printk(KERN_INFO "cmpci: unloading\n"); - pci_unregister_driver(&cm_driver); -} - -module_init(init_cmpci); -module_exit(cleanup_cmpci); diff --git a/sound/oss/cs4281/Makefile b/sound/oss/cs4281/Makefile deleted file mode 100644 index 6d527e8530..0000000000 --- a/sound/oss/cs4281/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# Makefile for Cirrus Logic-Crystal CS4281 -# - -obj-$(CONFIG_SOUND_CS4281) += cs4281.o - -cs4281-objs += cs4281m.o diff --git a/sound/oss/cs4281/cs4281_hwdefs.h b/sound/oss/cs4281/cs4281_hwdefs.h deleted file mode 100644 index 701d595e33..0000000000 --- a/sound/oss/cs4281/cs4281_hwdefs.h +++ /dev/null @@ -1,1234 +0,0 @@ -//**************************************************************************** -// -// HWDEFS.H - Definitions of the registers and data structures used by the -// CS4281 -// -// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp. -// -//**************************************************************************** - -#ifndef _H_HWDEFS -#define _H_HWDEFS - -//**************************************************************************** -// -// The following define the offsets of the registers located in the PCI -// configuration space of the CS4281 part. -// -//**************************************************************************** -#define PCICONFIG_DEVID_VENID 0x00000000L -#define PCICONFIG_STATUS_COMMAND 0x00000004L -#define PCICONFIG_CLASS_REVISION 0x00000008L -#define PCICONFIG_LATENCY_TIMER 0x0000000CL -#define PCICONFIG_BA0 0x00000010L -#define PCICONFIG_BA1 0x00000014L -#define PCICONFIG_SUBSYSID_SUBSYSVENID 0x0000002CL -#define PCICONFIG_INTERRUPT 0x0000003CL - -//**************************************************************************** -// -// The following define the offsets of the registers accessed via base address -// register zero on the CS4281 part. -// -//**************************************************************************** -#define BA0_HISR 0x00000000L -#define BA0_HICR 0x00000008L -#define BA0_HIMR 0x0000000CL -#define BA0_IIER 0x00000010L -#define BA0_HDSR0 0x000000F0L -#define BA0_HDSR1 0x000000F4L -#define BA0_HDSR2 0x000000F8L -#define BA0_HDSR3 0x000000FCL -#define BA0_DCA0 0x00000110L -#define BA0_DCC0 0x00000114L -#define BA0_DBA0 0x00000118L -#define BA0_DBC0 0x0000011CL -#define BA0_DCA1 0x00000120L -#define BA0_DCC1 0x00000124L -#define BA0_DBA1 0x00000128L -#define BA0_DBC1 0x0000012CL -#define BA0_DCA2 0x00000130L -#define BA0_DCC2 0x00000134L -#define BA0_DBA2 0x00000138L -#define BA0_DBC2 0x0000013CL -#define BA0_DCA3 0x00000140L -#define BA0_DCC3 0x00000144L -#define BA0_DBA3 0x00000148L -#define BA0_DBC3 0x0000014CL -#define BA0_DMR0 0x00000150L -#define BA0_DCR0 0x00000154L -#define BA0_DMR1 0x00000158L -#define BA0_DCR1 0x0000015CL -#define BA0_DMR2 0x00000160L -#define BA0_DCR2 0x00000164L -#define BA0_DMR3 0x00000168L -#define BA0_DCR3 0x0000016CL -#define BA0_DLMR 0x00000170L -#define BA0_DLSR 0x00000174L -#define BA0_FCR0 0x00000180L -#define BA0_FCR1 0x00000184L -#define BA0_FCR2 0x00000188L -#define BA0_FCR3 0x0000018CL -#define BA0_FPDR0 0x00000190L -#define BA0_FPDR1 0x00000194L -#define BA0_FPDR2 0x00000198L -#define BA0_FPDR3 0x0000019CL -#define BA0_FCHS 0x0000020CL -#define BA0_FSIC0 0x00000210L -#define BA0_FSIC1 0x00000214L -#define BA0_FSIC2 0x00000218L -#define BA0_FSIC3 0x0000021CL -#define BA0_PCICFG00 0x00000300L -#define BA0_PCICFG04 0x00000304L -#define BA0_PCICFG08 0x00000308L -#define BA0_PCICFG0C 0x0000030CL -#define BA0_PCICFG10 0x00000310L -#define BA0_PCICFG14 0x00000314L -#define BA0_PCICFG18 0x00000318L -#define BA0_PCICFG1C 0x0000031CL -#define BA0_PCICFG20 0x00000320L -#define BA0_PCICFG24 0x00000324L -#define BA0_PCICFG28 0x00000328L -#define BA0_PCICFG2C 0x0000032CL -#define BA0_PCICFG30 0x00000330L -#define BA0_PCICFG34 0x00000334L -#define BA0_PCICFG38 0x00000338L -#define BA0_PCICFG3C 0x0000033CL -#define BA0_PCICFG40 0x00000340L -#define BA0_PMCS 0x00000344L -#define BA0_CWPR 0x000003E0L -#define BA0_EPPMC 0x000003E4L -#define BA0_GPIOR 0x000003E8L -#define BA0_SPMC 0x000003ECL -#define BA0_CFLR 0x000003F0L -#define BA0_IISR 0x000003F4L -#define BA0_TMS 0x000003F8L -#define BA0_SSVID 0x000003FCL -#define BA0_CLKCR1 0x00000400L -#define BA0_FRR 0x00000410L -#define BA0_SLT12O 0x0000041CL -#define BA0_SERMC 0x00000420L -#define BA0_SERC1 0x00000428L -#define BA0_SERC2 0x0000042CL -#define BA0_SLT12M 0x0000045CL -#define BA0_ACCTL 0x00000460L -#define BA0_ACSTS 0x00000464L -#define BA0_ACOSV 0x00000468L -#define BA0_ACCAD 0x0000046CL -#define BA0_ACCDA 0x00000470L -#define BA0_ACISV 0x00000474L -#define BA0_ACSAD 0x00000478L -#define BA0_ACSDA 0x0000047CL -#define BA0_JSPT 0x00000480L -#define BA0_JSCTL 0x00000484L -#define BA0_MIDCR 0x00000490L -#define BA0_MIDCMD 0x00000494L -#define BA0_MIDSR 0x00000494L -#define BA0_MIDWP 0x00000498L -#define BA0_MIDRP 0x0000049CL -#define BA0_AODSD1 0x000004A8L -#define BA0_AODSD2 0x000004ACL -#define BA0_CFGI 0x000004B0L -#define BA0_SLT12M2 0x000004DCL -#define BA0_ACSTS2 0x000004E4L -#define BA0_ACISV2 0x000004F4L -#define BA0_ACSAD2 0x000004F8L -#define BA0_ACSDA2 0x000004FCL -#define BA0_IOTGP 0x00000500L -#define BA0_IOTSB 0x00000504L -#define BA0_IOTFM 0x00000508L -#define BA0_IOTDMA 0x0000050CL -#define BA0_IOTAC0 0x00000500L -#define BA0_IOTAC1 0x00000504L -#define BA0_IOTAC2 0x00000508L -#define BA0_IOTAC3 0x0000050CL -#define BA0_IOTPCP 0x0000052CL -#define BA0_IOTCC 0x00000530L -#define BA0_IOTCR 0x0000058CL -#define BA0_PCPRR 0x00000600L -#define BA0_PCPGR 0x00000604L -#define BA0_PCPCR 0x00000608L -#define BA0_PCPCIEN 0x00000608L -#define BA0_SBMAR 0x00000700L -#define BA0_SBMDR 0x00000704L -#define BA0_SBRR 0x00000708L -#define BA0_SBRDP 0x0000070CL -#define BA0_SBWDP 0x00000710L -#define BA0_SBWBS 0x00000710L -#define BA0_SBRBS 0x00000714L -#define BA0_FMSR 0x00000730L -#define BA0_B0AP 0x00000730L -#define BA0_FMDP 0x00000734L -#define BA0_B1AP 0x00000738L -#define BA0_B1DP 0x0000073CL -#define BA0_SSPM 0x00000740L -#define BA0_DACSR 0x00000744L -#define BA0_ADCSR 0x00000748L -#define BA0_SSCR 0x0000074CL -#define BA0_FMLVC 0x00000754L -#define BA0_FMRVC 0x00000758L -#define BA0_SRCSA 0x0000075CL -#define BA0_PPLVC 0x00000760L -#define BA0_PPRVC 0x00000764L -#define BA0_PASR 0x00000768L -#define BA0_CASR 0x0000076CL - -//**************************************************************************** -// -// The following define the offsets of the AC97 shadow registers, which appear -// as a virtual extension to the base address register zero memory range. -// -//**************************************************************************** -#define AC97_REG_OFFSET_MASK 0x0000007EL -#define AC97_CODEC_NUMBER_MASK 0x00003000L - -#define BA0_AC97_RESET 0x00001000L -#define BA0_AC97_MASTER_VOLUME 0x00001002L -#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L -#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L -#define BA0_AC97_MASTER_TONE 0x00001008L -#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL -#define BA0_AC97_PHONE_VOLUME 0x0000100CL -#define BA0_AC97_MIC_VOLUME 0x0000100EL -#define BA0_AC97_LINE_IN_VOLUME 0x00001010L -#define BA0_AC97_CD_VOLUME 0x00001012L -#define BA0_AC97_VIDEO_VOLUME 0x00001014L -#define BA0_AC97_AUX_VOLUME 0x00001016L -#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L -#define BA0_AC97_RECORD_SELECT 0x0000101AL -#define BA0_AC97_RECORD_GAIN 0x0000101CL -#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL -#define BA0_AC97_GENERAL_PURPOSE 0x00001020L -#define BA0_AC97_3D_CONTROL 0x00001022L -#define BA0_AC97_MODEM_RATE 0x00001024L -#define BA0_AC97_POWERDOWN 0x00001026L -#define BA0_AC97_EXT_AUDIO_ID 0x00001028L -#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL -#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL -#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL -#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L -#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L -#define BA0_AC97_MIC_ADC_RATE 0x00001034L -#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L -#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L -#define BA0_AC97_RESERVED_3A 0x0000103AL -#define BA0_AC97_EXT_MODEM_ID 0x0000103CL -#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL -#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L -#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L -#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L -#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L -#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L -#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL -#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL -#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL -#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L -#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L -#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L -#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L -#define BA0_AC97_RESERVED_58 0x00001058L -#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL -#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL -#define BA0_AC97_AC_MODE 0x0000105EL -#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L -#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L -#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L -#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L -#define BA0_AC97_SPDIF_CONTROL 0x00001068L -#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL -#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL -#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL -#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L -#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L -#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L -#define BA0_AC97_CAL_ADDRESS 0x00001076L -#define BA0_AC97_CAL_DATA 0x00001078L -#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL -#define BA0_AC97_VENDOR_ID1 0x0000107CL -#define BA0_AC97_VENDOR_ID2 0x0000107EL - -//**************************************************************************** -// -// The following define the offsets of the registers and memories accessed via -// base address register one on the CS4281 part. -// -//**************************************************************************** - -//**************************************************************************** -// -// The following defines are for the flags in the PCI device ID/vendor ID -// register. -// -//**************************************************************************** -#define PDV_VENID_MASK 0x0000FFFFL -#define PDV_DEVID_MASK 0xFFFF0000L -#define PDV_VENID_SHIFT 0L -#define PDV_DEVID_SHIFT 16L -#define VENID_CIRRUS_LOGIC 0x1013L -#define DEVID_CS4281 0x6005L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI status and command -// register. -// -//**************************************************************************** -#define PSC_IO_SPACE_ENABLE 0x00000001L -#define PSC_MEMORY_SPACE_ENABLE 0x00000002L -#define PSC_BUS_MASTER_ENABLE 0x00000004L -#define PSC_SPECIAL_CYCLES 0x00000008L -#define PSC_MWI_ENABLE 0x00000010L -#define PSC_VGA_PALETTE_SNOOP 0x00000020L -#define PSC_PARITY_RESPONSE 0x00000040L -#define PSC_WAIT_CONTROL 0x00000080L -#define PSC_SERR_ENABLE 0x00000100L -#define PSC_FAST_B2B_ENABLE 0x00000200L -#define PSC_UDF_MASK 0x007F0000L -#define PSC_FAST_B2B_CAPABLE 0x00800000L -#define PSC_PARITY_ERROR_DETECTED 0x01000000L -#define PSC_DEVSEL_TIMING_MASK 0x06000000L -#define PSC_TARGET_ABORT_SIGNALLED 0x08000000L -#define PSC_RECEIVED_TARGET_ABORT 0x10000000L -#define PSC_RECEIVED_MASTER_ABORT 0x20000000L -#define PSC_SIGNALLED_SERR 0x40000000L -#define PSC_DETECTED_PARITY_ERROR 0x80000000L -#define PSC_UDF_SHIFT 16L -#define PSC_DEVSEL_TIMING_SHIFT 25L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI class/revision ID -// register. -// -//**************************************************************************** -#define PCR_REVID_MASK 0x000000FFL -#define PCR_INTERFACE_MASK 0x0000FF00L -#define PCR_SUBCLASS_MASK 0x00FF0000L -#define PCR_CLASS_MASK 0xFF000000L -#define PCR_REVID_SHIFT 0L -#define PCR_INTERFACE_SHIFT 8L -#define PCR_SUBCLASS_SHIFT 16L -#define PCR_CLASS_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI latency timer register. -// -//**************************************************************************** -#define PLT_CACHE_LINE_SIZE_MASK 0x000000FFL -#define PLT_LATENCY_TIMER_MASK 0x0000FF00L -#define PLT_HEADER_TYPE_MASK 0x00FF0000L -#define PLT_BIST_MASK 0xFF000000L -#define PLT_CACHE_LINE_SIZE_SHIFT 0L -#define PLT_LATENCY_TIMER_SHIFT 8L -#define PLT_HEADER_TYPE_SHIFT 16L -#define PLT_BIST_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI base address registers. -// -//**************************************************************************** -#define PBAR_MEMORY_SPACE_INDICATOR 0x00000001L -#define PBAR_LOCATION_TYPE_MASK 0x00000006L -#define PBAR_NOT_PREFETCHABLE 0x00000008L -#define PBAR_ADDRESS_MASK 0xFFFFFFF0L -#define PBAR_LOCATION_TYPE_SHIFT 1L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI subsystem ID/subsystem -// vendor ID register. -// -//**************************************************************************** -#define PSS_SUBSYSTEM_VENDOR_ID_MASK 0x0000FFFFL -#define PSS_SUBSYSTEM_ID_MASK 0xFFFF0000L -#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT 0L -#define PSS_SUBSYSTEM_ID_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the PCI interrupt register. -// -//**************************************************************************** -#define PI_LINE_MASK 0x000000FFL -#define PI_PIN_MASK 0x0000FF00L -#define PI_MIN_GRANT_MASK 0x00FF0000L -#define PI_MAX_LATENCY_MASK 0xFF000000L -#define PI_LINE_SHIFT 0L -#define PI_PIN_SHIFT 8L -#define PI_MIN_GRANT_SHIFT 16L -#define PI_MAX_LATENCY_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the host interrupt status -// register. -// -//**************************************************************************** -#define HISR_HVOLMASK 0x00000003L -#define HISR_VDNI 0x00000001L -#define HISR_VUPI 0x00000002L -#define HISR_GP1I 0x00000004L -#define HISR_GP3I 0x00000008L -#define HISR_GPSI 0x00000010L -#define HISR_GPPI 0x00000020L -#define HISR_DMAI 0x00040000L -#define HISR_FIFOI 0x00100000L -#define HISR_HVOL 0x00200000L -#define HISR_MIDI 0x00400000L -#define HISR_SBINT 0x00800000L -#define HISR_INTENA 0x80000000L -#define HISR_DMA_MASK 0x00000F00L -#define HISR_FIFO_MASK 0x0000F000L -#define HISR_DMA_SHIFT 8L -#define HISR_FIFO_SHIFT 12L -#define HISR_FIFO0 0x00001000L -#define HISR_FIFO1 0x00002000L -#define HISR_FIFO2 0x00004000L -#define HISR_FIFO3 0x00008000L -#define HISR_DMA0 0x00000100L -#define HISR_DMA1 0x00000200L -#define HISR_DMA2 0x00000400L -#define HISR_DMA3 0x00000800L -#define HISR_RESERVED 0x40000000L - -//**************************************************************************** -// -// The following defines are for the flags in the host interrupt control -// register. -// -//**************************************************************************** -#define HICR_IEV 0x00000001L -#define HICR_CHGM 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the DMA Mode Register n -// (DMRn) -// -//**************************************************************************** -#define DMRn_TR_MASK 0x0000000CL -#define DMRn_TR_SHIFT 2L -#define DMRn_AUTO 0x00000010L -#define DMRn_TR_READ 0x00000008L -#define DMRn_TR_WRITE 0x00000004L -#define DMRn_TYPE_MASK 0x000000C0L -#define DMRn_TYPE_SHIFT 6L -#define DMRn_SIZE8 0x00010000L -#define DMRn_MONO 0x00020000L -#define DMRn_BEND 0x00040000L -#define DMRn_USIGN 0x00080000L -#define DMRn_SIZE20 0x00100000L -#define DMRn_SWAPC 0x00400000L -#define DMRn_CBC 0x01000000L -#define DMRn_TBC 0x02000000L -#define DMRn_POLL 0x10000000L -#define DMRn_DMA 0x20000000L -#define DMRn_FSEL_MASK 0xC0000000L -#define DMRn_FSEL_SHIFT 30L -#define DMRn_FSEL0 0x00000000L -#define DMRn_FSEL1 0x40000000L -#define DMRn_FSEL2 0x80000000L -#define DMRn_FSEL3 0xC0000000L - -//**************************************************************************** -// -// The following defines are for the flags in the DMA Command Register n -// (DCRn) -// -//**************************************************************************** -#define DCRn_HTCIE 0x00020000L -#define DCRn_TCIE 0x00010000L -#define DCRn_MSK 0x00000001L - -//**************************************************************************** -// -// The following defines are for the flags in the FIFO Control -// register n.(FCRn) -// -//**************************************************************************** -#define FCRn_OF_MASK 0x0000007FL -#define FCRn_OF_SHIFT 0L -#define FCRn_SZ_MASK 0x00007F00L -#define FCRn_SZ_SHIFT 8L -#define FCRn_LS_MASK 0x001F0000L -#define FCRn_LS_SHIFT 16L -#define FCRn_RS_MASK 0x1F000000L -#define FCRn_RS_SHIFT 24L -#define FCRn_FEN 0x80000000L -#define FCRn_PSH 0x20000000L -#define FCRn_DACZ 0x40000000L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port Power Management -// control register.(SPMC) -// -//**************************************************************************** -#define SPMC_RSTN 0x00000001L -#define SPMC_ASYN 0x00000002L -#define SPMC_WUP1 0x00000004L -#define SPMC_WUP2 0x00000008L -#define SPMC_ASDI2E 0x00000100L -#define SPMC_ESSPD 0x00000200L -#define SPMC_GISPEN 0x00004000L -#define SPMC_GIPPEN 0x00008000L - -//**************************************************************************** -// -// The following defines are for the flags in the Configuration Load register. -// (CFLR) -// -//**************************************************************************** -#define CFLR_CLOCK_SOURCE_MASK 0x00000003L -#define CFLR_CLOCK_SOURCE_AC97 0x00000001L - -#define CFLR_CB0_MASK 0x000000FFL -#define CFLR_CB1_MASK 0x0000FF00L -#define CFLR_CB2_MASK 0x00FF0000L -#define CFLR_CB3_MASK 0xFF000000L -#define CFLR_CB0_SHIFT 0L -#define CFLR_CB1_SHIFT 8L -#define CFLR_CB2_SHIFT 16L -#define CFLR_CB3_SHIFT 24L - -#define IOTCR_DMA0 0x00000000L -#define IOTCR_DMA1 0x00000400L -#define IOTCR_DMA2 0x00000800L -#define IOTCR_DMA3 0x00000C00L -#define IOTCR_CCLS 0x00000100L -#define IOTCR_PCPCI 0x00000200L -#define IOTCR_DDMA 0x00000300L - -#define SBWBS_WBB 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the SRC Slot Assignment Register -// (SRCSA) -// -//**************************************************************************** -#define SRCSA_PLSS_MASK 0x0000001FL -#define SRCSA_PLSS_SHIFT 0L -#define SRCSA_PRSS_MASK 0x00001F00L -#define SRCSA_PRSS_SHIFT 8L -#define SRCSA_CLSS_MASK 0x001F0000L -#define SRCSA_CLSS_SHIFT 16L -#define SRCSA_CRSS_MASK 0x1F000000L -#define SRCSA_CRSS_SHIFT 24L - -//**************************************************************************** -// -// The following defines are for the flags in the Sound System Power Management -// register.(SSPM) -// -//**************************************************************************** -#define SSPM_FPDN 0x00000080L -#define SSPM_MIXEN 0x00000040L -#define SSPM_CSRCEN 0x00000020L -#define SSPM_PSRCEN 0x00000010L -#define SSPM_JSEN 0x00000008L -#define SSPM_ACLEN 0x00000004L -#define SSPM_FMEN 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the Sound System Control -// Register. (SSCR) -// -//**************************************************************************** -#define SSCR_SB 0x00000004L -#define SSCR_HVC 0x00000008L -#define SSCR_LPFIFO 0x00000040L -#define SSCR_LPSRC 0x00000080L -#define SSCR_XLPSRC 0x00000100L -#define SSCR_MVMD 0x00010000L -#define SSCR_MVAD 0x00020000L -#define SSCR_MVLD 0x00040000L -#define SSCR_MVCS 0x00080000L - -//**************************************************************************** -// -// The following defines are for the flags in the Clock Control Register 1. -// (CLKCR1) -// -//**************************************************************************** -#define CLKCR1_DLLSS_MASK 0x0000000CL -#define CLKCR1_DLLSS_SHIFT 2L -#define CLKCR1_DLLP 0x00000010L -#define CLKCR1_SWCE 0x00000020L -#define CLKCR1_DLLOS 0x00000040L -#define CLKCR1_CKRA 0x00010000L -#define CLKCR1_CKRN 0x00020000L -#define CLKCR1_DLLRDY 0x01000000L -#define CLKCR1_CLKON 0x02000000L - -//**************************************************************************** -// -// The following defines are for the flags in the Sound Blaster Read Buffer -// Status.(SBRBS) -// -//**************************************************************************** -#define SBRBS_RD_MASK 0x0000007FL -#define SBRBS_RD_SHIFT 0L -#define SBRBS_RBF 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port master control -// register.(SERMC) -// -//**************************************************************************** -#define SERMC_MSPE 0x00000001L -#define SERMC_PTC_MASK 0x0000000EL -#define SERMC_PTC_SHIFT 1L -#define SERMC_PTC_AC97 0x00000002L -#define SERMC_PLB 0x00000010L -#define SERMC_PXLB 0x00000020L -#define SERMC_LOFV 0x00080000L -#define SERMC_SLB 0x00100000L -#define SERMC_SXLB 0x00200000L -#define SERMC_ODSEN1 0x01000000L -#define SERMC_ODSEN2 0x02000000L - -//**************************************************************************** -// -// The following defines are for the flags in the General Purpose I/O Register. -// (GPIOR) -// -//**************************************************************************** -#define GPIOR_VDNS 0x00000001L -#define GPIOR_VUPS 0x00000002L -#define GPIOR_GP1S 0x00000004L -#define GPIOR_GP3S 0x00000008L -#define GPIOR_GPSS 0x00000010L -#define GPIOR_GPPS 0x00000020L -#define GPIOR_GP1D 0x00000400L -#define GPIOR_GP3D 0x00000800L -#define GPIOR_VDNLT 0x00010000L -#define GPIOR_VDNPO 0x00020000L -#define GPIOR_VDNST 0x00040000L -#define GPIOR_VDNW 0x00080000L -#define GPIOR_VUPLT 0x00100000L -#define GPIOR_VUPPO 0x00200000L -#define GPIOR_VUPST 0x00400000L -#define GPIOR_VUPW 0x00800000L -#define GPIOR_GP1OE 0x01000000L -#define GPIOR_GP1PT 0x02000000L -#define GPIOR_GP1ST 0x04000000L -#define GPIOR_GP1W 0x08000000L -#define GPIOR_GP3OE 0x10000000L -#define GPIOR_GP3PT 0x20000000L -#define GPIOR_GP3ST 0x40000000L -#define GPIOR_GP3W 0x80000000L - -//**************************************************************************** -// -// The following defines are for the flags in the clock control register 1. -// -//**************************************************************************** -#define CLKCR1_PLLSS_MASK 0x0000000CL -#define CLKCR1_PLLSS_SERIAL 0x00000000L -#define CLKCR1_PLLSS_CRYSTAL 0x00000004L -#define CLKCR1_PLLSS_PCI 0x00000008L -#define CLKCR1_PLLSS_RESERVED 0x0000000CL -#define CLKCR1_PLLP 0x00000010L -#define CLKCR1_SWCE 0x00000020L -#define CLKCR1_PLLOS 0x00000040L - -//**************************************************************************** -// -// The following defines are for the flags in the feature reporting register. -// -//**************************************************************************** -#define FRR_FAB_MASK 0x00000003L -#define FRR_MASK_MASK 0x0000001CL -#define FRR_ID_MASK 0x00003000L -#define FRR_FAB_SHIFT 0L -#define FRR_MASK_SHIFT 2L -#define FRR_ID_SHIFT 12L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port 1 configuration -// register. -// -//**************************************************************************** -#define SERC1_VALUE 0x00000003L -#define SERC1_SO1EN 0x00000001L -#define SERC1_SO1F_MASK 0x0000000EL -#define SERC1_SO1F_CS423X 0x00000000L -#define SERC1_SO1F_AC97 0x00000002L -#define SERC1_SO1F_DAC 0x00000004L -#define SERC1_SO1F_SPDIF 0x00000006L - -//**************************************************************************** -// -// The following defines are for the flags in the serial port 2 configuration -// register. -// -//**************************************************************************** -#define SERC2_VALUE 0x00000003L -#define SERC2_SI1EN 0x00000001L -#define SERC2_SI1F_MASK 0x0000000EL -#define SERC2_SI1F_CS423X 0x00000000L -#define SERC2_SI1F_AC97 0x00000002L -#define SERC2_SI1F_ADC 0x00000004L -#define SERC2_SI1F_SPDIF 0x00000006L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 control register. -// -//**************************************************************************** -#define ACCTL_ESYN 0x00000002L -#define ACCTL_VFRM 0x00000004L -#define ACCTL_DCV 0x00000008L -#define ACCTL_CRW 0x00000010L -#define ACCTL_TC 0x00000040L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status register. -// -//**************************************************************************** -#define ACSTS_CRDY 0x00000001L -#define ACSTS_VSTS 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 output slot valid -// register. -// -//**************************************************************************** -#define ACOSV_SLV3 0x00000001L -#define ACOSV_SLV4 0x00000002L -#define ACOSV_SLV5 0x00000004L -#define ACOSV_SLV6 0x00000008L -#define ACOSV_SLV7 0x00000010L -#define ACOSV_SLV8 0x00000020L -#define ACOSV_SLV9 0x00000040L -#define ACOSV_SLV10 0x00000080L -#define ACOSV_SLV11 0x00000100L -#define ACOSV_SLV12 0x00000200L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 command address -// register. -// -//**************************************************************************** -#define ACCAD_CI_MASK 0x0000007FL -#define ACCAD_CI_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 command data register. -// -//**************************************************************************** -#define ACCDA_CD_MASK 0x0000FFFFL -#define ACCDA_CD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 input slot valid -// register. -// -//**************************************************************************** -#define ACISV_ISV3 0x00000001L -#define ACISV_ISV4 0x00000002L -#define ACISV_ISV5 0x00000004L -#define ACISV_ISV6 0x00000008L -#define ACISV_ISV7 0x00000010L -#define ACISV_ISV8 0x00000020L -#define ACISV_ISV9 0x00000040L -#define ACISV_ISV10 0x00000080L -#define ACISV_ISV11 0x00000100L -#define ACISV_ISV12 0x00000200L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status address -// register. -// -//**************************************************************************** -#define ACSAD_SI_MASK 0x0000007FL -#define ACSAD_SI_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status data register. -// -//**************************************************************************** -#define ACSDA_SD_MASK 0x0000FFFFL -#define ACSDA_SD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers (all 12). -// -//**************************************************************************** -#define IOTAC_SA_MASK 0x0000FFFFL -#define IOTAC_MSK_MASK 0x000F0000L -#define IOTAC_IODC_MASK 0x06000000L -#define IOTAC_IODC_16_BIT 0x00000000L -#define IOTAC_IODC_10_BIT 0x02000000L -#define IOTAC_IODC_12_BIT 0x04000000L -#define IOTAC_WSPI 0x08000000L -#define IOTAC_RSPI 0x10000000L -#define IOTAC_WSE 0x20000000L -#define IOTAC_WE 0x40000000L -#define IOTAC_RE 0x80000000L -#define IOTAC_SA_SHIFT 0L -#define IOTAC_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI master enable -// register. -// -//**************************************************************************** -#define PCPCIEN_EN 0x00000001L - -//**************************************************************************** -// -// The following defines are for the flags in the joystick poll/trigger -// register. -// -//**************************************************************************** -#define JSPT_CAX 0x00000001L -#define JSPT_CAY 0x00000002L -#define JSPT_CBX 0x00000004L -#define JSPT_CBY 0x00000008L -#define JSPT_BA1 0x00000010L -#define JSPT_BA2 0x00000020L -#define JSPT_BB1 0x00000040L -#define JSPT_BB2 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the joystick control register. -// The TBF bit has been moved from MIDSR register to JSCTL register bit 8. -// -//**************************************************************************** -#define JSCTL_SP_MASK 0x00000003L -#define JSCTL_SP_SLOW 0x00000000L -#define JSCTL_SP_MEDIUM_SLOW 0x00000001L -#define JSCTL_SP_MEDIUM_FAST 0x00000002L -#define JSCTL_SP_FAST 0x00000003L -#define JSCTL_ARE 0x00000004L -#define JSCTL_TBF 0x00000100L - - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI control register. -// -//**************************************************************************** -#define MIDCR_TXE 0x00000001L -#define MIDCR_RXE 0x00000002L -#define MIDCR_RIE 0x00000004L -#define MIDCR_TIE 0x00000008L -#define MIDCR_MLB 0x00000010L -#define MIDCR_MRST 0x00000020L - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI status register. -// -//**************************************************************************** -#define MIDSR_RBE 0x00000080L -#define MIDSR_RDA 0x00008000L - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI write port register. -// -//**************************************************************************** -#define MIDWP_MWD_MASK 0x000000FFL -#define MIDWP_MWD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the MIDI read port register. -// -//**************************************************************************** -#define MIDRP_MRD_MASK 0x000000FFL -#define MIDRP_MRD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the configuration interface -// register. -// -//**************************************************************************** -#define CFGI_CLK 0x00000001L -#define CFGI_DOUT 0x00000002L -#define CFGI_DIN_EEN 0x00000004L -#define CFGI_EELD 0x00000008L - -//**************************************************************************** -// -// The following defines are for the flags in the subsystem ID and vendor ID -// register. -// -//**************************************************************************** -#define SSVID_VID_MASK 0x0000FFFFL -#define SSVID_SID_MASK 0xFFFF0000L -#define SSVID_VID_SHIFT 0L -#define SSVID_SID_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the GPIO pin interface register. -// -//**************************************************************************** -#define GPIOR_VOLDN 0x00000001L -#define GPIOR_VOLUP 0x00000002L -#define GPIOR_SI2D 0x00000004L -#define GPIOR_SI2OE 0x00000008L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status register 2. -// -//**************************************************************************** -#define ACSTS2_CRDY 0x00000001L -#define ACSTS2_VSTS 0x00000002L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 input slot valid -// register 2. -// -//**************************************************************************** -#define ACISV2_ISV3 0x00000001L -#define ACISV2_ISV4 0x00000002L -#define ACISV2_ISV5 0x00000004L -#define ACISV2_ISV6 0x00000008L -#define ACISV2_ISV7 0x00000010L -#define ACISV2_ISV8 0x00000020L -#define ACISV2_ISV9 0x00000040L -#define ACISV2_ISV10 0x00000080L -#define ACISV2_ISV11 0x00000100L -#define ACISV2_ISV12 0x00000200L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status address -// register 2. -// -//**************************************************************************** -#define ACSAD2_SI_MASK 0x0000007FL -#define ACSAD2_SI_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 status data register 2. -// -//**************************************************************************** -#define ACSDA2_SD_MASK 0x0000FFFFL -#define ACSDA2_SD_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap control register. -// -//**************************************************************************** -#define IOTCR_ITD 0x00000001L -#define IOTCR_HRV 0x00000002L -#define IOTCR_SRV 0x00000004L -#define IOTCR_DTI 0x00000008L -#define IOTCR_DFI 0x00000010L -#define IOTCR_DDP 0x00000020L -#define IOTCR_JTE 0x00000040L -#define IOTCR_PPE 0x00000080L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers for Hardware Master Volume. -// -//**************************************************************************** -#define IOTGP_SA_MASK 0x0000FFFFL -#define IOTGP_MSK_MASK 0x000F0000L -#define IOTGP_IODC_MASK 0x06000000L -#define IOTGP_IODC_16_BIT 0x00000000L -#define IOTGP_IODC_10_BIT 0x02000000L -#define IOTGP_IODC_12_BIT 0x04000000L -#define IOTGP_WSPI 0x08000000L -#define IOTGP_RSPI 0x10000000L -#define IOTGP_WSE 0x20000000L -#define IOTGP_WE 0x40000000L -#define IOTGP_RE 0x80000000L -#define IOTGP_SA_SHIFT 0L -#define IOTGP_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers for Sound Blaster -// -//**************************************************************************** -#define IOTSB_SA_MASK 0x0000FFFFL -#define IOTSB_MSK_MASK 0x000F0000L -#define IOTSB_IODC_MASK 0x06000000L -#define IOTSB_IODC_16_BIT 0x00000000L -#define IOTSB_IODC_10_BIT 0x02000000L -#define IOTSB_IODC_12_BIT 0x04000000L -#define IOTSB_WSPI 0x08000000L -#define IOTSB_RSPI 0x10000000L -#define IOTSB_WSE 0x20000000L -#define IOTSB_WE 0x40000000L -#define IOTSB_RE 0x80000000L -#define IOTSB_SA_SHIFT 0L -#define IOTSB_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the I/O trap address and control -// registers for FM. -// -//**************************************************************************** -#define IOTFM_SA_MASK 0x0000FFFFL -#define IOTFM_MSK_MASK 0x000F0000L -#define IOTFM_IODC_MASK 0x06000000L -#define IOTFM_IODC_16_BIT 0x00000000L -#define IOTFM_IODC_10_BIT 0x02000000L -#define IOTFM_IODC_12_BIT 0x04000000L -#define IOTFM_WSPI 0x08000000L -#define IOTFM_RSPI 0x10000000L -#define IOTFM_WSE 0x20000000L -#define IOTFM_WE 0x40000000L -#define IOTFM_RE 0x80000000L -#define IOTFM_SA_SHIFT 0L -#define IOTFM_MSK_SHIFT 16L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI request register. -// -//**************************************************************************** -#define PCPRR_RDC_MASK 0x00000007L -#define PCPRR_REQ 0x00008000L -#define PCPRR_RDC_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI grant register. -// -//**************************************************************************** -#define PCPGR_GDC_MASK 0x00000007L -#define PCPGR_VL 0x00008000L -#define PCPGR_GDC_SHIFT 0L - -//**************************************************************************** -// -// The following defines are for the flags in the PC/PCI Control Register. -// -//**************************************************************************** -#define PCPCR_EN 0x00000001L - -//**************************************************************************** -// -// The following defines are for the flags in the debug index register. -// -//**************************************************************************** -#define DREG_REGID_MASK 0x0000007FL -#define DREG_DEBUG 0x00000080L -#define DREG_RGBK_MASK 0x00000700L -#define DREG_TRAP 0x00000800L -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_TRAPX 0x00001000L -#endif -#endif -#define DREG_REGID_SHIFT 0L -#define DREG_RGBK_SHIFT 8L -#define DREG_RGBK_REGID_MASK 0x0000077FL -#define DREG_REGID_R0 0x00000010L -#define DREG_REGID_R1 0x00000011L -#define DREG_REGID_R2 0x00000012L -#define DREG_REGID_R3 0x00000013L -#define DREG_REGID_R4 0x00000014L -#define DREG_REGID_R5 0x00000015L -#define DREG_REGID_R6 0x00000016L -#define DREG_REGID_R7 0x00000017L -#define DREG_REGID_R8 0x00000018L -#define DREG_REGID_R9 0x00000019L -#define DREG_REGID_RA 0x0000001AL -#define DREG_REGID_RB 0x0000001BL -#define DREG_REGID_RC 0x0000001CL -#define DREG_REGID_RD 0x0000001DL -#define DREG_REGID_RE 0x0000001EL -#define DREG_REGID_RF 0x0000001FL -#define DREG_REGID_RA_BUS_LOW 0x00000020L -#define DREG_REGID_RA_BUS_HIGH 0x00000038L -#define DREG_REGID_YBUS_LOW 0x00000050L -#define DREG_REGID_YBUS_HIGH 0x00000058L -#define DREG_REGID_TRAP_0 0x00000100L -#define DREG_REGID_TRAP_1 0x00000101L -#define DREG_REGID_TRAP_2 0x00000102L -#define DREG_REGID_TRAP_3 0x00000103L -#define DREG_REGID_TRAP_4 0x00000104L -#define DREG_REGID_TRAP_5 0x00000105L -#define DREG_REGID_TRAP_6 0x00000106L -#define DREG_REGID_TRAP_7 0x00000107L -#define DREG_REGID_INDIRECT_ADDRESS 0x0000010EL -#define DREG_REGID_TOP_OF_STACK 0x0000010FL -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_8 0x00000110L -#define DREG_REGID_TRAP_9 0x00000111L -#define DREG_REGID_TRAP_10 0x00000112L -#define DREG_REGID_TRAP_11 0x00000113L -#define DREG_REGID_TRAP_12 0x00000114L -#define DREG_REGID_TRAP_13 0x00000115L -#define DREG_REGID_TRAP_14 0x00000116L -#define DREG_REGID_TRAP_15 0x00000117L -#define DREG_REGID_TRAP_16 0x00000118L -#define DREG_REGID_TRAP_17 0x00000119L -#define DREG_REGID_TRAP_18 0x0000011AL -#define DREG_REGID_TRAP_19 0x0000011BL -#define DREG_REGID_TRAP_20 0x0000011CL -#define DREG_REGID_TRAP_21 0x0000011DL -#define DREG_REGID_TRAP_22 0x0000011EL -#define DREG_REGID_TRAP_23 0x0000011FL -#endif -#endif -#define DREG_REGID_RSA0_LOW 0x00000200L -#define DREG_REGID_RSA0_HIGH 0x00000201L -#define DREG_REGID_RSA1_LOW 0x00000202L -#define DREG_REGID_RSA1_HIGH 0x00000203L -#define DREG_REGID_RSA2 0x00000204L -#define DREG_REGID_RSA3 0x00000205L -#define DREG_REGID_RSI0_LOW 0x00000206L -#define DREG_REGID_RSI0_HIGH 0x00000207L -#define DREG_REGID_RSI1 0x00000208L -#define DREG_REGID_RSI2 0x00000209L -#define DREG_REGID_SAGUSTATUS 0x0000020AL -#define DREG_REGID_RSCONFIG01_LOW 0x0000020BL -#define DREG_REGID_RSCONFIG01_HIGH 0x0000020CL -#define DREG_REGID_RSCONFIG23_LOW 0x0000020DL -#define DREG_REGID_RSCONFIG23_HIGH 0x0000020EL -#define DREG_REGID_RSDMA01E 0x0000020FL -#define DREG_REGID_RSDMA23E 0x00000210L -#define DREG_REGID_RSD0_LOW 0x00000211L -#define DREG_REGID_RSD0_HIGH 0x00000212L -#define DREG_REGID_RSD1_LOW 0x00000213L -#define DREG_REGID_RSD1_HIGH 0x00000214L -#define DREG_REGID_RSD2_LOW 0x00000215L -#define DREG_REGID_RSD2_HIGH 0x00000216L -#define DREG_REGID_RSD3_LOW 0x00000217L -#define DREG_REGID_RSD3_HIGH 0x00000218L -#define DREG_REGID_SRAR_HIGH 0x0000021AL -#define DREG_REGID_SRAR_LOW 0x0000021BL -#define DREG_REGID_DMA_STATE 0x0000021CL -#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021DL -#define DREG_REGID_NEXT_DMA_STREAM 0x0000021EL -#define DREG_REGID_CPU_STATUS 0x00000300L -#define DREG_REGID_MAC_MODE 0x00000301L -#define DREG_REGID_STACK_AND_REPEAT 0x00000302L -#define DREG_REGID_INDEX0 0x00000304L -#define DREG_REGID_INDEX1 0x00000305L -#define DREG_REGID_DMA_STATE_0_3 0x00000400L -#define DREG_REGID_DMA_STATE_4_7 0x00000404L -#define DREG_REGID_DMA_STATE_8_11 0x00000408L -#define DREG_REGID_DMA_STATE_12_15 0x0000040CL -#define DREG_REGID_DMA_STATE_16_19 0x00000410L -#define DREG_REGID_DMA_STATE_20_23 0x00000414L -#define DREG_REGID_DMA_STATE_24_27 0x00000418L -#define DREG_REGID_DMA_STATE_28_31 0x0000041CL -#define DREG_REGID_DMA_STATE_32_35 0x00000420L -#define DREG_REGID_DMA_STATE_36_39 0x00000424L -#define DREG_REGID_DMA_STATE_40_43 0x00000428L -#define DREG_REGID_DMA_STATE_44_47 0x0000042CL -#define DREG_REGID_DMA_STATE_48_51 0x00000430L -#define DREG_REGID_DMA_STATE_52_55 0x00000434L -#define DREG_REGID_DMA_STATE_56_59 0x00000438L -#define DREG_REGID_DMA_STATE_60_63 0x0000043CL -#define DREG_REGID_DMA_STATE_64_67 0x00000440L -#define DREG_REGID_DMA_STATE_68_71 0x00000444L -#define DREG_REGID_DMA_STATE_72_75 0x00000448L -#define DREG_REGID_DMA_STATE_76_79 0x0000044CL -#define DREG_REGID_DMA_STATE_80_83 0x00000450L -#define DREG_REGID_DMA_STATE_84_87 0x00000454L -#define DREG_REGID_DMA_STATE_88_91 0x00000458L -#define DREG_REGID_DMA_STATE_92_95 0x0000045CL -#define DREG_REGID_TRAP_SELECT 0x00000500L -#define DREG_REGID_TRAP_WRITE_0 0x00000500L -#define DREG_REGID_TRAP_WRITE_1 0x00000501L -#define DREG_REGID_TRAP_WRITE_2 0x00000502L -#define DREG_REGID_TRAP_WRITE_3 0x00000503L -#define DREG_REGID_TRAP_WRITE_4 0x00000504L -#define DREG_REGID_TRAP_WRITE_5 0x00000505L -#define DREG_REGID_TRAP_WRITE_6 0x00000506L -#define DREG_REGID_TRAP_WRITE_7 0x00000507L -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_WRITE_8 0x00000510L -#define DREG_REGID_TRAP_WRITE_9 0x00000511L -#define DREG_REGID_TRAP_WRITE_10 0x00000512L -#define DREG_REGID_TRAP_WRITE_11 0x00000513L -#define DREG_REGID_TRAP_WRITE_12 0x00000514L -#define DREG_REGID_TRAP_WRITE_13 0x00000515L -#define DREG_REGID_TRAP_WRITE_14 0x00000516L -#define DREG_REGID_TRAP_WRITE_15 0x00000517L -#define DREG_REGID_TRAP_WRITE_16 0x00000518L -#define DREG_REGID_TRAP_WRITE_17 0x00000519L -#define DREG_REGID_TRAP_WRITE_18 0x0000051AL -#define DREG_REGID_TRAP_WRITE_19 0x0000051BL -#define DREG_REGID_TRAP_WRITE_20 0x0000051CL -#define DREG_REGID_TRAP_WRITE_21 0x0000051DL -#define DREG_REGID_TRAP_WRITE_22 0x0000051EL -#define DREG_REGID_TRAP_WRITE_23 0x0000051FL -#endif -#endif -#define DREG_REGID_MAC0_ACC0_LOW 0x00000600L -#define DREG_REGID_MAC0_ACC1_LOW 0x00000601L -#define DREG_REGID_MAC0_ACC2_LOW 0x00000602L -#define DREG_REGID_MAC0_ACC3_LOW 0x00000603L -#define DREG_REGID_MAC1_ACC0_LOW 0x00000604L -#define DREG_REGID_MAC1_ACC1_LOW 0x00000605L -#define DREG_REGID_MAC1_ACC2_LOW 0x00000606L -#define DREG_REGID_MAC1_ACC3_LOW 0x00000607L -#define DREG_REGID_MAC0_ACC0_MID 0x00000608L -#define DREG_REGID_MAC0_ACC1_MID 0x00000609L -#define DREG_REGID_MAC0_ACC2_MID 0x0000060AL -#define DREG_REGID_MAC0_ACC3_MID 0x0000060BL -#define DREG_REGID_MAC1_ACC0_MID 0x0000060CL -#define DREG_REGID_MAC1_ACC1_MID 0x0000060DL -#define DREG_REGID_MAC1_ACC2_MID 0x0000060EL -#define DREG_REGID_MAC1_ACC3_MID 0x0000060FL -#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610L -#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611L -#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612L -#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613L -#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614L -#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615L -#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616L -#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617L -#define DREG_REGID_RSHOUT_LOW 0x00000620L -#define DREG_REGID_RSHOUT_MID 0x00000628L -#define DREG_REGID_RSHOUT_HIGH 0x00000630L - -//**************************************************************************** -// -// The following defines are for the flags in the AC97 S/PDIF Control register. -// -//**************************************************************************** -#define SPDIF_CONTROL_SPDIF_EN 0x00008000L -#define SPDIF_CONTROL_VAL 0x00004000L -#define SPDIF_CONTROL_COPY 0x00000004L -#define SPDIF_CONTROL_CC0 0x00000010L -#define SPDIF_CONTROL_CC1 0x00000020L -#define SPDIF_CONTROL_CC2 0x00000040L -#define SPDIF_CONTROL_CC3 0x00000080L -#define SPDIF_CONTROL_CC4 0x00000100L -#define SPDIF_CONTROL_CC5 0x00000200L -#define SPDIF_CONTROL_CC6 0x00000400L -#define SPDIF_CONTROL_L 0x00000800L - -#endif // _H_HWDEFS diff --git a/sound/oss/cs4281/cs4281_wrapper-24.c b/sound/oss/cs4281/cs4281_wrapper-24.c deleted file mode 100644 index 4559f02c99..0000000000 --- a/sound/oss/cs4281/cs4281_wrapper-24.c +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* -* -* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.com). -* -* 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. -* -* 12/20/00 trw - new file. -* -*******************************************************************************/ - -#include - -static int cs4281_resume_null(struct pci_dev *pcidev) { return 0; } -static int cs4281_suspend_null(struct pci_dev *pcidev, pm_message_t state) { return 0; } - -#define free_dmabuf(state, dmabuf) \ - pci_free_consistent(state->pcidev, \ - PAGE_SIZE << (dmabuf)->buforder, \ - (dmabuf)->rawbuf, (dmabuf)->dmaaddr); -#define free_dmabuf2(state, dmabuf) \ - pci_free_consistent((state)->pcidev, \ - PAGE_SIZE << (state)->buforder_tmpbuff, \ - (state)->tmpbuff, (state)->dmaaddr_tmpbuff); -#define cs4x_pgoff(vma) ((vma)->vm_pgoff) - diff --git a/sound/oss/cs4281/cs4281m.c b/sound/oss/cs4281/cs4281m.c deleted file mode 100644 index 0400a416dc..0000000000 --- a/sound/oss/cs4281/cs4281m.c +++ /dev/null @@ -1,4487 +0,0 @@ -/******************************************************************************* -* -* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- adapted from drivers by Thomas Sailer, -* -- but don't bug him; Problems should go to: -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.com). -* -* 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. -* -* Module command line parameters: -* none -* -* Supported devices: -* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible -* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible -* /dev/midi simple MIDI UART interface, no ioctl -* -* Modification History -* 08/20/00 trw - silence and no stopping DAC until release -* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. -* 09/18/00 trw - added 16bit only record with conversion -* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous -* capture/playback rates) -* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin -* libOSSm.so) -* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) -* 11/03/00 trw - fixed interrupt loss/stutter, added debug. -* 11/10/00 bkz - added __devinit to cs4281_hw_init() -* 11/10/00 trw - fixed SMP and capture spinlock hang. -* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. -* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. -* 12/08/00 trw - added PM support. -* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 -* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. -* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. -* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use -* defaultorder-100 as power of 2 for the buffer size. example: -* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. -* -*******************************************************************************/ - -/* uncomment the following line to disable building PM support into the driver */ -//#define NOT_CS4281_PM 1 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -//#include "cs_dm.h" -#include "cs4281_hwdefs.h" -#include "cs4281pm.h" - -struct cs4281_state; - -static void stop_dac(struct cs4281_state *s); -static void stop_adc(struct cs4281_state *s); -static void start_dac(struct cs4281_state *s); -static void start_adc(struct cs4281_state *s); -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -// --------------------------------------------------------------------- - -#ifndef PCI_VENDOR_ID_CIRRUS -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#endif -#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281 -#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 -#endif - -#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS) -#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ - -// buffer order determines the size of the dma buffer for the driver. -// under Linux, a smaller buffer allows more responsiveness from many of the -// applications (e.g. games). A larger buffer allows some of the apps (esound) -// to not underrun the dma buffer as easily. As default, use 32k (order=3) -// rather than 64k as some of the games work more responsively. -// log base 2( buff sz = 32k). -static unsigned long defaultorder = 3; -module_param(defaultorder, ulong, 0); - -// -// Turn on/off debugging compilation by commenting out "#define CSDEBUG" -// -#define CSDEBUG 1 -#if CSDEBUG -#define CSDEBUG_INTERFACE 1 -#else -#undef CSDEBUG_INTERFACE -#endif -// -// cs_debugmask areas -// -#define CS_INIT 0x00000001 // initialization and probe functions -#define CS_ERROR 0x00000002 // tmp debugging bit placeholder -#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) -#define CS_FUNCTION 0x00000008 // enter/leave functions -#define CS_WAVE_WRITE 0x00000010 // write information for wave -#define CS_WAVE_READ 0x00000020 // read information for wave -#define CS_MIDI_WRITE 0x00000040 // write information for midi -#define CS_MIDI_READ 0x00000080 // read information for midi -#define CS_MPU401_WRITE 0x00000100 // write information for mpu401 -#define CS_MPU401_READ 0x00000200 // read information for mpu401 -#define CS_OPEN 0x00000400 // all open functions in the driver -#define CS_RELEASE 0x00000800 // all release functions in the driver -#define CS_PARMS 0x00001000 // functional and operational parameters -#define CS_IOCTL 0x00002000 // ioctl (non-mixer) -#define CS_PM 0x00004000 // power management -#define CS_TMP 0x10000000 // tmp debug mask bit - -#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend -#define CS_IOCTL_CMD_RESUME 0x2 // resume -// -// CSDEBUG is usual mode is set to 1, then use the -// cs_debuglevel and cs_debugmask to turn on or off debugging. -// Debug level of 1 has been defined to be kernel errors and info -// that should be printed on any released driver. -// -#if CSDEBUG -#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} -#else -#define CS_DBGOUT(mask,level,x) -#endif - -#if CSDEBUG -static unsigned long cs_debuglevel = 1; // levels range from 1-9 -static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values -module_param(cs_debuglevel, ulong, 0); -module_param(cs_debugmask, ulong, 0); -#endif -#define CS_TRUE 1 -#define CS_FALSE 0 - -// MIDI buffer sizes -#define MIDIINBUF 500 -#define MIDIOUTBUF 500 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define CS4281_MAJOR_VERSION 1 -#define CS4281_MINOR_VERSION 13 -#ifdef __ia64__ -#define CS4281_ARCH 64 //architecture key -#else -#define CS4281_ARCH 32 //architecture key -#endif - -#define CS_TYPE_ADC 0 -#define CS_TYPE_DAC 1 - - -static const char invalid_magic[] = - KERN_CRIT "cs4281: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != CS4281_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -//LIST_HEAD(cs4281_devs); -static struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs }; - -struct cs4281_state; - -#include "cs4281_wrapper-24.c" - -struct cs4281_state { - // magic - unsigned int magic; - - // we keep the cards in a linked list - struct cs4281_state *next; - - // pcidev is needed to turn off the DDMA controller at driver shutdown - struct pci_dev *pcidev; - struct list_head list; - - // soundcore stuff - int dev_audio; - int dev_mixer; - int dev_midi; - - // hardware resources - unsigned int pBA0phys, pBA1phys; - char __iomem *pBA0; - char __iomem *pBA1; - unsigned int irq; - - // mixer registers - struct { - unsigned short vol[10]; - unsigned int recsrc; - unsigned int modcnt; - unsigned short micpreamp; - } mix; - - // wave stuff - struct properties { - unsigned fmt; - unsigned fmt_original; // original requested format - unsigned channels; - unsigned rate; - unsigned char clkdiv; - } prop_dac, prop_adc; - unsigned conversion:1; // conversion from 16 to 8 bit in progress - void *tmpbuff; // tmp buffer for sample conversions - unsigned ena; - spinlock_t lock; - struct mutex open_sem; - struct mutex open_sem_adc; - struct mutex open_sem_dac; - mode_t open_mode; - wait_queue_head_t open_wait; - wait_queue_head_t open_wait_adc; - wait_queue_head_t open_wait_dac; - - dma_addr_t dmaaddr_tmpbuff; - unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. - struct dmabuf { - void *rawbuf; // Physical address of - dma_addr_t dmaaddr; - unsigned buforder; // Log base 2 of 'rawbuf' size in bytes.. - unsigned numfrag; // # of 'fragments' in the buffer. - unsigned fragshift; // Log base 2 of fragment size. - unsigned hwptr, swptr; - unsigned total_bytes; // # bytes process since open. - unsigned blocks; // last returned blocks value GETOPTR - unsigned wakeup; // interrupt occurred on block - int count; - unsigned underrun; // underrun flag - unsigned error; // over/underrun - wait_queue_head_t wait; - // redundant, but makes calculations easier - unsigned fragsize; // 2**fragshift.. - unsigned dmasize; // 2**buforder. - unsigned fragsamples; - // OSS stuff - unsigned mapped:1; // Buffer mapped in cs4281_mmap()? - unsigned ready:1; // prog_dmabuf_dac()/adc() successful? - unsigned endcleared:1; - unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - - // midi stuff - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - - struct cs4281_pm pm; - struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; -}; - -#include "cs4281pm-24.c" - -#if CSDEBUG - -// DEBUG ROUTINES - -#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) -#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) -#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) -#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) - -#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) - - -static void cs_printioctl(unsigned int x) -{ - unsigned int i; - unsigned char vidx; - // Index of mixtable1[] member is Device ID - // and must be <= SOUND_MIXER_NRDEVICES. - // Value of array member is index into s->mix.vol[] - static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 1, // voice - [SOUND_MIXER_LINE1] = 2, // AUX - [SOUND_MIXER_CD] = 3, // CD - [SOUND_MIXER_LINE] = 4, // Line - [SOUND_MIXER_SYNTH] = 5, // FM - [SOUND_MIXER_MIC] = 6, // Mic - [SOUND_MIXER_SPEAKER] = 7, // Speaker - [SOUND_MIXER_RECLEV] = 8, // Recording level - [SOUND_MIXER_VOLUME] = 9 // Master Volume - }; - - switch (x) { - case SOUND_MIXER_CS_GETDBGMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_GETDBGMASK:\n")); - break; - case SOUND_MIXER_CS_GETDBGLEVEL: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); - break; - case SOUND_MIXER_CS_SETDBGMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_SETDBGMASK:\n")); - break; - case SOUND_MIXER_CS_SETDBGLEVEL: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); - break; - case OSS_GETVERSION: - CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); - break; - case SNDCTL_DSP_SYNC: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); - break; - case SNDCTL_DSP_SETDUPLEX: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); - break; - case SNDCTL_DSP_GETCAPS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); - break; - case SNDCTL_DSP_RESET: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); - break; - case SNDCTL_DSP_SPEED: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); - break; - case SNDCTL_DSP_STEREO: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); - break; - case SNDCTL_DSP_CHANNELS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); - break; - case SNDCTL_DSP_GETFMTS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); - break; - case SNDCTL_DSP_SETFMT: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); - break; - case SNDCTL_DSP_POST: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); - break; - case SNDCTL_DSP_GETTRIGGER: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); - break; - case SNDCTL_DSP_SETTRIGGER: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); - break; - case SNDCTL_DSP_GETOSPACE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); - break; - case SNDCTL_DSP_GETISPACE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); - break; - case SNDCTL_DSP_NONBLOCK: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); - break; - case SNDCTL_DSP_GETODELAY: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); - break; - case SNDCTL_DSP_GETIPTR: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); - break; - case SNDCTL_DSP_GETOPTR: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); - break; - case SNDCTL_DSP_GETBLKSIZE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); - break; - case SNDCTL_DSP_SETFRAGMENT: - CS_DBGOUT(CS_IOCTL, 4, - printk("SNDCTL_DSP_SETFRAGMENT:\n")); - break; - case SNDCTL_DSP_SUBDIVIDE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); - break; - case SOUND_PCM_READ_RATE: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); - break; - case SOUND_PCM_READ_CHANNELS: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_PCM_READ_CHANNELS:\n")); - break; - case SOUND_PCM_READ_BITS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); - break; - case SOUND_PCM_WRITE_FILTER: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_PCM_WRITE_FILTER:\n")); - break; - case SNDCTL_DSP_SETSYNCRO: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); - break; - case SOUND_PCM_READ_FILTER: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); - break; - case SOUND_MIXER_PRIVATE1: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); - break; - case SOUND_MIXER_PRIVATE2: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); - break; - case SOUND_MIXER_PRIVATE3: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); - break; - case SOUND_MIXER_PRIVATE4: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); - break; - case SOUND_MIXER_PRIVATE5: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); - break; - case SOUND_MIXER_INFO: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); - break; - case SOUND_OLD_MIXER_INFO: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); - break; - - default: - switch (_IOC_NR(x)) { - case SOUND_MIXER_VOLUME: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_VOLUME:\n")); - break; - case SOUND_MIXER_SPEAKER: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_SPEAKER:\n")); - break; - case SOUND_MIXER_RECLEV: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_RECLEV:\n")); - break; - case SOUND_MIXER_MIC: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_MIC:\n")); - break; - case SOUND_MIXER_SYNTH: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_SYNTH:\n")); - break; - case SOUND_MIXER_RECSRC: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_RECSRC:\n")); - break; - case SOUND_MIXER_DEVMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_DEVMASK:\n")); - break; - case SOUND_MIXER_RECMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_RECMASK:\n")); - break; - case SOUND_MIXER_STEREODEVS: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_STEREODEVS:\n")); - break; - case SOUND_MIXER_CAPS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); - break; - default: - i = _IOC_NR(x); - if (i >= SOUND_MIXER_NRDEVICES - || !(vidx = mixtable1[i])) { - CS_DBGOUT(CS_IOCTL, 4, printk - ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", - x, i)); - } else { - CS_DBGOUT(CS_IOCTL, 4, printk - ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", - x, i)); - } - break; - } - } -} -#endif -static int prog_dmabuf_adc(struct cs4281_state *s); -static void prog_codec(struct cs4281_state *s, unsigned type); - -// --------------------------------------------------------------------- -// -// Hardware Interfaces For the CS4281 -// - - -//****************************************************************************** -// "delayus()-- Delay for the specified # of microseconds. -//****************************************************************************** -static void delayus(struct cs4281_state *s, u32 delay) -{ - u32 j; - if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { - j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ - if (j < 1) - j = 1; /* minimum one jiffy. */ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(j); - } else - udelay(delay); - return; -} - - -//****************************************************************************** -// "cs4281_read_ac97" -- Reads a word from the specified location in the -// CS4281's address space(based on the BA0 register). -// -// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address -// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register, -// 0h for reads. -// 3. Write ACCTL = Control Register = 460h for initiating the write -// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h -// 5. if DCV not cleared, break and return error -// 6. Read ACSTS = Status Register = 464h, check VSTS bit -//**************************************************************************** -static int cs4281_read_ac97(struct cs4281_state *card, u32 offset, - u32 * value) -{ - u32 count, status; - - // Make sure that there is not data sitting - // around from a previous uncompleted access. - // ACSDA = Status Data Register = 47Ch - status = readl(card->pBA0 + BA0_ACSDA); - - // Setup the AC97 control registers on the CS4281 to send the - // appropriate command to the AC97 to perform the read. - // ACCAD = Command Address Register = 46Ch - // ACCDA = Command Data Register = 470h - // ACCTL = Control Register = 460h - // bit DCV - will clear when process completed - // bit CRW - Read command - // bit VFRM - valid frame enabled - // bit ESYN - ASYNC generation enabled - - // Get the actual AC97 register from the offset - writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); - writel(0, card->pBA0 + BA0_ACCDA); - writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN, - card->pBA0 + BA0_ACCTL); - - // Wait for the read to occur. - for (count = 0; count < 10; count++) { - // First, we want to wait for a short time. - udelay(25); - - // Now, check to see if the read has completed. - // ACCTL = 460h, DCV should be reset by now and 460h = 17h - if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)) - break; - } - - // Make sure the read completed. - if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV) - return 1; - - // Wait for the valid status bit to go active. - for (count = 0; count < 10; count++) { - // Read the AC97 status register. - // ACSTS = Status Register = 464h - status = readl(card->pBA0 + BA0_ACSTS); - - // See if we have valid status. - // VSTS - Valid Status - if (status & ACSTS_VSTS) - break; - // Wait for a short while. - udelay(25); - } - - // Make sure we got valid status. - if (!(status & ACSTS_VSTS)) - return 1; - - // Read the data returned from the AC97 register. - // ACSDA = Status Data Register = 474h - *value = readl(card->pBA0 + BA0_ACSDA); - - // Success. - return (0); -} - - -//**************************************************************************** -// -// "cs4281_write_ac97()"-- writes a word to the specified location in the -// CS461x's address space (based on the part's base address zero register). -// -// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address -// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg. -// 3. Write ACCTL = Control Register = 460h for initiating the write -// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h -// 5. if DCV not cleared, break and return error -// -//**************************************************************************** -static int cs4281_write_ac97(struct cs4281_state *card, u32 offset, - u32 value) -{ - u32 count, status=0; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n")); - - // Setup the AC97 control registers on the CS4281 to send the - // appropriate command to the AC97 to perform the read. - // ACCAD = Command Address Register = 46Ch - // ACCDA = Command Data Register = 470h - // ACCTL = Control Register = 460h - // set DCV - will clear when process completed - // reset CRW - Write command - // set VFRM - valid frame enabled - // set ESYN - ASYNC generation enabled - // set RSTN - ARST# inactive, AC97 codec not reset - - // Get the actual AC97 register from the offset - - writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); - writel(value, card->pBA0 + BA0_ACCDA); - writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN, - card->pBA0 + BA0_ACCTL); - - // Wait for the write to occur. - for (count = 0; count < 100; count++) { - // First, we want to wait for a short time. - udelay(25); - // Now, check to see if the write has completed. - // ACCTL = 460h, DCV should be reset by now and 460h = 07h - status = readl(card->pBA0 + BA0_ACCTL); - if (!(status & ACCTL_DCV)) - break; - } - - // Make sure the write completed. - if (status & ACCTL_DCV) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n")); - return 1; - } - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n")); - // Success. - return 0; -} - - -//****************************************************************************** -// "Init4281()" -- Bring up the part. -//****************************************************************************** -static __devinit int cs4281_hw_init(struct cs4281_state *card) -{ - u32 ac97_slotid; - u32 temp1, temp2; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n")); -#ifndef NOT_CS4281_PM - if(!card) - return 1; -#endif - temp2 = readl(card->pBA0 + BA0_CFLR); - CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2)); - if(temp2 != CS4281_CFLR_DEFAULT) - { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n", - temp2,CS4281_CFLR_DEFAULT)); - writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR); - temp2 = readl(card->pBA0 + BA0_CFLR); - if(temp2 != CS4281_CFLR_DEFAULT) - { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n")); - return 1; - } - } - - //***************************************7 - // Set up the Sound System Configuration - //*************************************** - - // Set the 'Configuration Write Protect' register - // to 4281h. Allows vendor-defined configuration - // space between 0e4h and 0ffh to be written. - - writel(0x4281, card->pBA0 + BA0_CWPR); // (3e0h) - - // (0), Blast the clock control register to zero so that the - // PLL starts out in a known state, and blast the master serial - // port control register to zero so that the serial ports also - // start out in a known state. - - writel(0, card->pBA0 + BA0_CLKCR1); // (400h) - writel(0, card->pBA0 + BA0_SERMC); // (420h) - - - // (1), Make ESYN go to zero to turn off - // the Sync pulse on the AC97 link. - - writel(0, card->pBA0 + BA0_ACCTL); - udelay(50); - - - // (2) Drive the ARST# pin low for a minimum of 1uS (as defined in - // the AC97 spec) and then drive it high. This is done for non - // AC97 modes since there might be logic external to the CS461x - // that uses the ARST# line for a reset. - - writel(0, card->pBA0 + BA0_SPMC); // (3ech) - udelay(100); - writel(SPMC_RSTN, card->pBA0 + BA0_SPMC); - delayus(card,50000); // Wait 50 ms for ABITCLK to become stable. - - // (3) Turn on the Sound System Clocks. - writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1); // (400h) - delayus(card,50000); // Wait for the PLL to stabilize. - // Turn on clocking of the core (CLKCR1(400h) = 0x00000030) - writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1); - - // (4) Power on everything for now.. - writel(0x7E, card->pBA0 + BA0_SSPM); // (740h) - - // (5) Wait for clock stabilization. - for (temp1 = 0; temp1 < 1000; temp1++) { - udelay(1000); - if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY) - break; - } - if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: DLLRDY failed!\n")); - return -EIO; - } - // (6) Enable ASYNC generation. - writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) - - // Now wait 'for a short while' to allow the AC97 - // part to start generating bit clock. (so we don't - // Try to start the PLL without an input clock.) - delayus(card,50000); - - // Set the serial port timing configuration, so that the - // clock control circuit gets its clock from the right place. - writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. - - // (7) Wait for the codec ready signal from the AC97 codec. - - for (temp1 = 0; temp1 < 1000; temp1++) { - // Delay a mil to let things settle out and - // to prevent retrying the read too quickly. - udelay(1000); - if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY) // If ready, (464h) - break; // exit the 'for' loop. - } - if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)) // If never came ready, - { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR - "cs4281: ACSTS never came ready!\n")); - return -EIO; // exit initialization. - } - // (8) Assert the 'valid frame' signal so we can - // begin sending commands to the AC97 codec. - writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) - - // (9), Wait until CODEC calibration is finished. - // Print an error message if it doesn't. - for (temp1 = 0; temp1 < 1000; temp1++) { - delayus(card,10000); - // Read the AC97 Powerdown Control/Status Register. - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2); - if ((temp2 & 0x0000000F) == 0x0000000F) - break; - } - if ((temp2 & 0x0000000F) != 0x0000000F) { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR - "cs4281: Codec failed to calibrate. Status = %.8x.\n", - temp2)); - return -EIO; - } - // (10), Set the serial port timing configuration, so that the - // clock control circuit gets its clock from the right place. - writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. - - - // (11) Wait until we've sampled input slots 3 & 4 as valid, meaning - // that the codec is pumping ADC data across the AC link. - for (temp1 = 0; temp1 < 1000; temp1++) { - // Delay a mil to let things settle out and - // to prevent retrying the read too quickly. - delayus(card,1000); //(test) - - // Read the input slot valid register; See - // if input slots 3 and 4 are valid yet. - if ( - (readl(card->pBA0 + BA0_ACISV) & - (ACISV_ISV3 | ACISV_ISV4)) == - (ACISV_ISV3 | ACISV_ISV4)) break; // Exit the 'for' if slots are valid. - } - // If we never got valid data, exit initialization. - if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) - != (ACISV_ISV3 | ACISV_ISV4)) { - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_ERR - "cs4281: Never got valid data!\n")); - return -EIO; // If no valid data, exit initialization. - } - // (12), Start digital data transfer of audio data to the codec. - writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV); // (468h) - - - //************************************** - // Unmute the Master and Alternate - // (headphone) volumes. Set to max. - //************************************** - cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0); - cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0); - - //****************************************** - // Power on the DAC(AddDACUser()from main()) - //****************************************** - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff); - - // Wait until we sample a DAC ready state. - for (temp2 = 0; temp2 < 32; temp2++) { - // Let's wait a mil to let things settle. - delayus(card,1000); - // Read the current state of the power control reg. - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - // If the DAC ready state bit is set, stop waiting. - if (temp1 & 0x2) - break; - } - - //****************************************** - // Power on the ADC(AddADCUser()from main()) - //****************************************** - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff); - - // Wait until we sample ADC ready state. - for (temp2 = 0; temp2 < 32; temp2++) { - // Let's wait a mil to let things settle. - delayus(card,1000); - // Read the current state of the power control reg. - cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); - // If the ADC ready state bit is set, stop waiting. - if (temp1 & 0x1) - break; - } - // Set up 4281 Register contents that - // don't change for boot duration. - - // For playback, we map AC97 slot 3 and 4(Left - // & Right PCM playback) to DMA Channel 0. - // Set the fifo to be 15 bytes at offset zero. - - ac97_slotid = 0x01000f00; // FCR0.RS[4:0]=1(=>slot4, right PCM playback). - // FCR0.LS[4:0]=0(=>slot3, left PCM playback). - // FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0. - writel(ac97_slotid, card->pBA0 + BA0_FCR0); // (180h) - writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0); // Turn on FIFO Enable. - - // For capture, we map AC97 slot 10 and 11(Left - // and Right PCM Record) to DMA Channel 1. - // Set the fifo to be 15 bytes at offset sixteen. - ac97_slotid = 0x0B0A0f10; // FCR1.RS[4:0]=11(=>slot11, right PCM record). - // FCR1.LS[4:0]=10(=>slot10, left PCM record). - // FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16. - writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1); // (184h) - writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1); // Turn on FIFO Enable. - - // Map the Playback SRC to the same AC97 slots(3 & 4-- - // --Playback left & right)as DMA channel 0. - // Map the record SRC to the same AC97 slots(10 & 11-- - // -- Record left & right) as DMA channel 1. - - ac97_slotid = 0x0b0a0100; // SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback). - // SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback). - // SCRSA.CRSS[4:0]=11(=>slot11, right PCM record) - // SCRSA.CLSS[4:0]=10(=>slot10, left PCM record). - writel(ac97_slotid, card->pBA0 + BA0_SRCSA); // (75ch) - - // Set 'Half Terminal Count Interrupt Enable' and 'Terminal - // Count Interrupt Enable' in DMA Control Registers 0 & 1. - // Set 'MSK' flag to 1 to keep the DMA engines paused. - temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK); // (00030001h) - writel(temp1, card->pBA0 + BA0_DCR0); // (154h - writel(temp1, card->pBA0 + BA0_DCR1); // (15ch) - - // Set 'Auto-Initialize Control' to 'enabled'; For playback, - // set 'Transfer Type Control'(TR[1:0]) to 'read transfer', - // for record, set Transfer Type Control to 'write transfer'. - // All other bits set to zero; Some will be changed @ transfer start. - temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); // (20000018h) - writel(temp1, card->pBA0 + BA0_DMR0); // (150h) - temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); // (20000014h) - writel(temp1, card->pBA0 + BA0_DMR1); // (158h) - - // Enable DMA interrupts generally, and - // DMA0 & DMA1 interrupts specifically. - temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff; - writel(temp1, card->pBA0 + BA0_HIMR); - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n")); - return 0; -} - -#ifndef NOT_CS4281_PM -static void printpm(struct cs4281_state *s) -{ - CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); - CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", - (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); - CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", - s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", - s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", - s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); - CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", - s->pm.u32SSCR,s->pm.u32SRCSA)); - CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", - s->pm.u32DacASR,s->pm.u32AdcASR)); - CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", - s->pm.u32DacSR,s->pm.u32AdcSR)); - CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", - s->pm.u32MIDCR_Save)); - -} -static void printpipe(struct cs4281_pipeline *pl) -{ - - CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); - CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n", - (unsigned)pl->flags,pl->number)); - CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n", - pl->u32DBAnValue,pl->u32DBCnValue)); - CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n", - pl->u32DMRnValue,pl->u32DCRnValue)); - CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n", - pl->u32DBAnAddress,pl->u32DBCnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n", - pl->u32DCCnAddress,pl->u32DCCnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n", - pl->u32DMRnAddress,pl->u32DCRnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n", - pl->u32HDSRnAddress,pl->u32DBAn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n", - pl->u32DBCn_Save,pl->u32DMRn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n", - pl->u32DCRn_Save,pl->u32DCCn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n", - pl->u32DCAn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n", - pl->u32FCRn_Save,pl->u32FSICn_Save)); - CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n", - pl->u32FCRnValue,pl->u32FSICnValue)); - CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n", - pl->u32FCRnAddress,pl->u32FSICnAddress)); - CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n", - pl->u32FPDRnValue,pl->u32FPDRnAddress)); -} -static void printpipelines(struct cs4281_state *s) -{ - int i; - for(i=0;ipl[i].flags & CS4281_PIPELINE_VALID) - { - printpipe(&s->pl[i]); - } - } -} -/**************************************************************************** -* -* Suspend - save the ac97 regs, mute the outputs and power down the part. -* -****************************************************************************/ -static void cs4281_ac97_suspend(struct cs4281_state *s) -{ - int Count,i; - - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n")); -/* -* change the state, save the current hwptr, then stop the dac/adc -*/ - s->pm.flags &= ~CS4281_PM_IDLE; - s->pm.flags |= CS4281_PM_SUSPENDING; - s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); - s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); - stop_dac(s); - stop_adc(s); - - for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE) - && (i < CS4281_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { - cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]); - } -/* -* Save the ac97 volume registers as well as the current powerdown state. -* Now, mute the all the outputs (master, headphone, and mono), as well -* as the PCM volume, in preparation for powering down the entire part. -*/ - cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume); - cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume); - cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono); - cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume); - - cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000); - cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000); - cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); - cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000); - - cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown); - cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose); - -/* -* And power down everything on the AC97 codec. -*/ - cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00); - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n")); -} - -/**************************************************************************** -* -* Resume - power up the part and restore its registers.. -* -****************************************************************************/ -static void cs4281_ac97_resume(struct cs4281_state *s) -{ - int Count,i; - - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n")); - -/* do not save the power state registers at this time - // - // If we saved away the power control registers, write them into the - // shadows so those saved values get restored instead of the current - // shadowed value. - // - if( bPowerStateSaved ) - { - PokeShadow( 0x26, ulSaveReg0x26 ); - bPowerStateSaved = FALSE; - } -*/ - -// -// First, we restore the state of the general purpose register. This -// contains the mic select (mic1 or mic2) and if we restore this after -// we restore the mic volume/boost state and mic2 was selected at -// suspend time, we will end up with a brief period of time where mic1 -// is selected with the volume/boost settings for mic2, causing -// acoustic feedback. So we restore the general purpose register -// first, thereby getting the correct mic selected before we restore -// the mic volume/boost. -// - cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose); - -// -// Now, while the outputs are still muted, restore the state of power -// on the AC97 part. -// - cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown); - -/* -* Restore just the first set of registers, from register number -* 0x02 to the register number that ulHighestRegToRestore specifies. -*/ - for( Count = 0x2, i=0; - (Count <= CS4281_AC97_HIGHESTREGTORESTORE) - && (i < CS4281_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { - cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]); - } - CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n")); -} - -/* do not save the power state registers at this time -**************************************************************************** -* -* SavePowerState - Save the power registers away. -* -**************************************************************************** -void -HWAC97codec::SavePowerState(void) -{ - ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n"); - - ulSaveReg0x26 = PeekShadow(0x26); - - // - // Note that we have saved registers that need to be restored during a - // resume instead of ulAC97Regs[]. - // - bPowerStateSaved = TRUE; - -} // SavePowerState -*/ - -static void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - /* - * We need to save the contents of the BASIC FIFO Registers. - */ - pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress); - pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress); -} -static void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - /* - * We need to restore the contents of the BASIC FIFO Registers. - */ - writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress); - writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress); -} -static void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - // - // We need to save the contents of the BASIC DMA Registers. - // - pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress); - pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress); - pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress); - pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress); - pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress); - pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress); -} -static void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) -{ - // - // We need to save the contents of the BASIC DMA Registers. - // - writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress); - writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress); - writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress); - writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress); - writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress); - writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress); -} - -static int cs4281_suspend(struct cs4281_state *s) -{ - int i; - u32 u32CLKCR1; - struct cs4281_pm *pm = &s->pm; - CS_DBGOUT(CS_PM | CS_FUNCTION, 9, - printk("cs4281: cs4281_suspend()+ flags=%d\n", - (unsigned)s->pm.flags)); -/* -* check the current state, only suspend if IDLE -*/ - if(!(s->pm.flags & CS4281_PM_IDLE)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 2, - printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n")); - return 1; - } - s->pm.flags &= ~CS4281_PM_IDLE; - s->pm.flags |= CS4281_PM_SUSPENDING; - -// -// Gershwin CLKRUN - Set CKRA -// - u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); - - pm->u32CLKCR1_SAVE = u32CLKCR1; - if(!(u32CLKCR1 & 0x00010000 ) ) - writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); - -// -// First, turn on the clocks (yikes) to the devices, so that they will -// respond when we try to save their state. -// - if(!(u32CLKCR1 & CLKCR1_SWCE)) - { - writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1); - } - - // - // Save the power state - // - pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM); - - // - // Disable interrupts. - // - writel(HICR_CHGM, s->pBA0 + BA0_HICR); - - // - // Save the PCM Playback Left and Right Volume Control. - // - pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC); - pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC); - - // - // Save the FM Synthesis Left and Right Volume Control. - // - pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC); - pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC); - - // - // Save the GPIOR value. - // - pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR); - - // - // Save the JSCTL value. - // - pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR); - - // - // Save Sound System Control Register - // - pm->u32SSCR = readl(s->pBA0 + BA0_SSCR); - - // - // Save SRC Slot Assinment register - // - pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA); - - // - // Save sample rate - // - pm->u32DacASR = readl(s->pBA0 + BA0_PASR); - pm->u32AdcASR = readl(s->pBA0 + BA0_CASR); - pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); - pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); - - // - // Loop through all of the PipeLines - // - for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) - { - if(s->pl[i].flags & CS4281_PIPELINE_VALID) - { - // - // Ask the DMAengines and FIFOs to Suspend. - // - cs4281_SuspendDMAengine(s,&s->pl[i]); - cs4281_SuspendFIFO(s,&s->pl[i]); - } - } - // - // We need to save the contents of the Midi Control Register. - // - pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR); -/* -* save off the AC97 part information -*/ - cs4281_ac97_suspend(s); - - // - // Turn off the serial ports. - // - writel(0, s->pBA0 + BA0_SERMC); - - // - // Power off FM, Joystick, AC link, - // - writel(0, s->pBA0 + BA0_SSPM); - - // - // DLL off. - // - writel(0, s->pBA0 + BA0_CLKCR1); - - // - // AC link off. - // - writel(0, s->pBA0 + BA0_SPMC); - - // - // Put the chip into D3(hot) state. - // - // PokeBA0(BA0_PMCS, 0x00000003); - - // - // Gershwin CLKRUN - Clear CKRA - // - u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); - writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1); - -#ifdef CSDEBUG - printpm(s); - printpipelines(s); -#endif - - s->pm.flags &= ~CS4281_PM_SUSPENDING; - s->pm.flags |= CS4281_PM_SUSPENDED; - - CS_DBGOUT(CS_PM | CS_FUNCTION, 9, - printk("cs4281: cs4281_suspend()- flags=%d\n", - (unsigned)s->pm.flags)); - return 0; -} - -static int cs4281_resume(struct cs4281_state *s) -{ - int i; - unsigned temp1; - u32 u32CLKCR1; - struct cs4281_pm *pm = &s->pm; - CS_DBGOUT(CS_PM | CS_FUNCTION, 4, - printk( "cs4281: cs4281_resume()+ flags=%d\n", - (unsigned)s->pm.flags)); - if(!(s->pm.flags & CS4281_PM_SUSPENDED)) - { - CS_DBGOUT(CS_PM | CS_ERROR, 2, - printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n")); - return 1; - } - s->pm.flags &= ~CS4281_PM_SUSPENDED; - s->pm.flags |= CS4281_PM_RESUMING; - -// -// Gershwin CLKRUN - Set CKRA -// - u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); - writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); - - // - // set the power state. - // - //old PokeBA0(BA0_PMCS, 0); - - // - // Program the clock circuit and serial ports. - // - temp1 = cs4281_hw_init(s); - if (temp1) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, - printk(KERN_ERR - "cs4281: resume cs4281_hw_init() error.\n")); - return -1; - } - - // - // restore the Power state - // - writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM); - - // - // Set post SRC mix setting (FM or ALT48K) - // - writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM); - - // - // Loop through all of the PipeLines - // - for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) - { - if(s->pl[i].flags & CS4281_PIPELINE_VALID) - { - // - // Ask the DMAengines and FIFOs to Resume. - // - cs4281_ResumeDMAengine(s,&s->pl[i]); - cs4281_ResumeFIFO(s,&s->pl[i]); - } - } - // - // We need to restore the contents of the Midi Control Register. - // - writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR); - - cs4281_ac97_resume(s); - // - // Restore the PCM Playback Left and Right Volume Control. - // - writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC); - writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC); - - // - // Restore the FM Synthesis Left and Right Volume Control. - // - writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC); - writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC); - - // - // Restore the JSCTL value. - // - writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL); - - // - // Restore the GPIOR register value. - // - writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR); - - // - // Restore Sound System Control Register - // - writel(pm->u32SSCR, s->pBA0 + BA0_SSCR); - - // - // Restore SRC Slot Assignment register - // - writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA); - - // - // Restore sample rate - // - writel(pm->u32DacASR, s->pBA0 + BA0_PASR); - writel(pm->u32AdcASR, s->pBA0 + BA0_CASR); - writel(pm->u32DacSR, s->pBA0 + BA0_DACSR); - writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR); - - // - // Restore CFL1/2 registers we saved to compensate for OEM bugs. - // - // PokeBA0(BA0_CFLR, ulConfig); - - // - // Gershwin CLKRUN - Clear CKRA - // - writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1); - - // - // Enable interrupts on the part. - // - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); - -#ifdef CSDEBUG - printpm(s); - printpipelines(s); -#endif -/* -* change the state, restore the current hwptrs, then stop the dac/adc -*/ - s->pm.flags |= CS4281_PM_IDLE; - s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED - | CS4281_PM_RESUMING | CS4281_PM_RESUMED); - - writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0); - writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1); - start_dac(s); - start_adc(s); - - CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n", - (unsigned)s->pm.flags)); - return 0; -} - -#endif - -//****************************************************************************** -// "cs4281_play_rate()" -- -//****************************************************************************** -static void cs4281_play_rate(struct cs4281_state *card, u32 playrate) -{ - u32 DACSRvalue = 1; - - // Based on the sample rate, program the DACSR register. - if (playrate == 8000) - DACSRvalue = 5; - if (playrate == 11025) - DACSRvalue = 4; - else if (playrate == 22050) - DACSRvalue = 2; - else if (playrate == 44100) - DACSRvalue = 1; - else if ((playrate <= 48000) && (playrate >= 6023)) - DACSRvalue = 24576000 / (playrate * 16); - else if (playrate < 6023) - // Not allowed by open. - return; - else if (playrate > 48000) - // Not allowed by open. - return; - CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO - "cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n", - DACSRvalue, playrate)); - // Write the 'sample rate select code' - // to the 'DAC Sample Rate' register. - writel(DACSRvalue, card->pBA0 + BA0_DACSR); // (744h) -} - -//****************************************************************************** -// "cs4281_record_rate()" -- Initialize the record sample rate converter. -//****************************************************************************** -static void cs4281_record_rate(struct cs4281_state *card, u32 outrate) -{ - u32 ADCSRvalue = 1; - - // - // Based on the sample rate, program the ADCSR register - // - if (outrate == 8000) - ADCSRvalue = 5; - if (outrate == 11025) - ADCSRvalue = 4; - else if (outrate == 22050) - ADCSRvalue = 2; - else if (outrate == 44100) - ADCSRvalue = 1; - else if ((outrate <= 48000) && (outrate >= 6023)) - ADCSRvalue = 24576000 / (outrate * 16); - else if (outrate < 6023) { - // Not allowed by open. - return; - } else if (outrate > 48000) { - // Not allowed by open. - return; - } - CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO - "cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n", - ADCSRvalue, outrate)); - // Write the 'sample rate select code - // to the 'ADC Sample Rate' register. - writel(ADCSRvalue, card->pBA0 + BA0_ADCSR); // (748h) -} - - - -static void stop_dac(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n")); - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_WRITE; - temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK; - writel(temp1, s->pBA0 + BA0_DCR0); - - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void start_dac(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: start_dac()+\n")); - spin_lock_irqsave(&s->lock, flags); - if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || - (s->dma_dac.count > 0 - && s->dma_dac.ready)) -#ifndef NOT_CS4281_PM - && (s->pm.flags & CS4281_PM_IDLE)) -#else -) -#endif - { - s->ena |= FMODE_WRITE; - temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK; // Clear DMA0 channel mask. - writel(temp1, s->pBA0 + BA0_DCR0); // Start DMA'ing. - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. - - writel(7, s->pBA0 + BA0_PPRVC); - writel(7, s->pBA0 + BA0_PPLVC); - CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO - "cs4281: start_dac(): writel 0x%x start dma\n", temp1)); - - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4281: start_dac()-\n")); -} - - -static void stop_adc(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4281: stop_adc()+\n")); - - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_READ; - - if (s->conversion == 1) { - s->conversion = 0; - s->prop_adc.fmt = s->prop_adc.fmt_original; - } - temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK; - writel(temp1, s->pBA0 + BA0_DCR1); - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4281: stop_adc()-\n")); -} - - -static void start_adc(struct cs4281_state *s) -{ - unsigned long flags; - unsigned temp1; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: start_adc()+\n")); - - if (!(s->ena & FMODE_READ) && - (s->dma_adc.mapped || s->dma_adc.count <= - (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize)) - && s->dma_adc.ready -#ifndef NOT_CS4281_PM - && (s->pm.flags & CS4281_PM_IDLE)) -#else -) -#endif - { - if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { - // - // now only use 16 bit capture, due to truncation issue - // in the chip, noticable distortion occurs. - // allocate buffer and then convert from 16 bit to - // 8 bit for the user buffer. - // - s->prop_adc.fmt_original = s->prop_adc.fmt; - if (s->prop_adc.fmt & AFMT_S8) { - s->prop_adc.fmt &= ~AFMT_S8; - s->prop_adc.fmt |= AFMT_S16_LE; - } - if (s->prop_adc.fmt & AFMT_U8) { - s->prop_adc.fmt &= ~AFMT_U8; - s->prop_adc.fmt |= AFMT_U16_LE; - } - // - // prog_dmabuf_adc performs a stop_adc() but that is - // ok since we really haven't started the DMA yet. - // - prog_codec(s, CS_TYPE_ADC); - - if (prog_dmabuf_adc(s) != 0) { - CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO - "cs4281: start_adc(): error in prog_dmabuf_adc\n")); - } - s->conversion = 1; - } - spin_lock_irqsave(&s->lock, flags); - s->ena |= FMODE_READ; - temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK; // Clear DMA1 channel mask bit. - writel(temp1, s->pBA0 + BA0_DCR1); // Start recording - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. - spin_unlock_irqrestore(&s->lock, flags); - - CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO - "cs4281: start_adc(): writel 0x%x \n", temp1)); - } - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: start_adc()-\n")); - -} - - -// --------------------------------------------------------------------- - -#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. - - -static void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) -{ - struct page *map, *mapend; - - if (db->rawbuf) { - // Undo prog_dmabuf()'s marking the pages as reserved - mapend = - virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - - 1); - for (map = virt_to_page(db->rawbuf); map <= mapend; map++) - ClearPageReserved(map); - free_dmabuf(s, db); - } - if (s->tmpbuff && (db->type == CS_TYPE_ADC)) { - // Undo prog_dmabuf()'s marking the pages as reserved - mapend = - virt_to_page(s->tmpbuff + - (PAGE_SIZE << s->buforder_tmpbuff) - 1); - for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) - ClearPageReserved(map); - free_dmabuf2(s, db); - } - s->tmpbuff = NULL; - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db) -{ - int order; - unsigned bytespersec, temp1; - unsigned bufs, sample_shift = 0; - struct page *map, *mapend; - unsigned long df; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_dmabuf()+\n")); - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = - db->endcleared = db->blocks = db->wakeup = db->underrun = 0; -/* -* check for order within limits, but do not overwrite value, check -* later for a fractional defaultorder (i.e. 100+). -*/ - if((defaultorder > 0) && (defaultorder < 12)) - df = defaultorder; - else - df = 1; - - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = df; order >= DMABUF_MINORDER; order--) - if ( (db->rawbuf = (void *) pci_alloc_consistent( - s->pcidev, PAGE_SIZE << order, &db-> dmaaddr))) - break; - if (!db->rawbuf) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: prog_dmabuf(): unable to allocate rawbuf\n")); - return -ENOMEM; - } - db->buforder = order; - // Now mark the pages as reserved; otherwise the - // remap_pfn_range() in cs4281_mmap doesn't work. - // 1. get index to last page in mem_map array for rawbuf. - mapend = virt_to_page(db->rawbuf + - (PAGE_SIZE << db->buforder) - 1); - - // 2. mark each physical page in range as 'reserved'. - for (map = virt_to_page(db->rawbuf); map <= mapend; map++) - SetPageReserved(map); - } - if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) { - for (order = df; order >= DMABUF_MINORDER; - order--) - if ( (s->tmpbuff = (void *) pci_alloc_consistent( - s->pcidev, PAGE_SIZE << order, - &s->dmaaddr_tmpbuff))) - break; - if (!s->tmpbuff) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: prog_dmabuf(): unable to allocate tmpbuff\n")); - return -ENOMEM; - } - s->buforder_tmpbuff = order; - // Now mark the pages as reserved; otherwise the - // remap_pfn_range() in cs4281_mmap doesn't work. - // 1. get index to last page in mem_map array for rawbuf. - mapend = virt_to_page(s->tmpbuff + - (PAGE_SIZE << s->buforder_tmpbuff) - 1); - - // 2. mark each physical page in range as 'reserved'. - for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) - SetPageReserved(map); - } - if (db->type == CS_TYPE_DAC) { - if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - sample_shift++; - if (s->prop_dac.channels > 1) - sample_shift++; - bytespersec = s->prop_dac.rate << sample_shift; - } else // CS_TYPE_ADC - { - if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - sample_shift++; - if (s->prop_adc.channels > 1) - sample_shift++; - bytespersec = s->prop_adc.rate << sample_shift; - } - bufs = PAGE_SIZE << db->buforder; - -/* -* added fractional "defaultorder" inputs. if >100 then use -* defaultorder-100 as power of 2 for the buffer size. example: -* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. -*/ - if(defaultorder >= 100) - { - bufs = 1 << (defaultorder-100); - } - -#define INTERRUPT_RATE_MS 100 // Interrupt rate in milliseconds. - db->numfrag = 2; -/* -* Nominal frag size(bytes/interrupt) -*/ - temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS); - db->fragshift = 8; // Min 256 bytes. - while (1 << db->fragshift < temp1) // Calc power of 2 frag size. - db->fragshift += 1; - db->fragsize = 1 << db->fragshift; - db->dmasize = db->fragsize * 2; - db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. - -// If the calculated size is larger than the allocated -// buffer, divide the allocated buffer into 2 fragments. - if (db->dmasize > bufs) { - - db->numfrag = 2; // Two fragments. - db->fragsize = bufs >> 1; // Each 1/2 the alloc'ed buffer. - db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. - db->dmasize = bufs; // Use all the alloc'ed buffer. - - db->fragshift = 0; // Calculate 'fragshift'. - temp1 = db->fragsize; // update_ptr() uses it - while ((temp1 >>= 1) > 1) // to calc 'total-bytes' - db->fragshift += 1; // returned in DSP_GETI/OPTR. - } - CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO - "cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n", - db->numfrag, db->fragsize, db->fragsamples, - db->fragshift, bufs, - (db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : - s->prop_adc.fmt, - (db->type == CS_TYPE_DAC) ? s->prop_dac.channels : - s->prop_adc.channels)); - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_dmabuf()-\n")); - return 0; -} - - -static int prog_dmabuf_adc(struct cs4281_state *s) -{ - unsigned long va; - unsigned count; - int c; - stop_adc(s); - s->dma_adc.type = CS_TYPE_ADC; - if ((c = prog_dmabuf(s, &s->dma_adc))) - return c; - - if (s->dma_adc.rawbuf) { - memset(s->dma_adc.rawbuf, - (s->prop_adc. - fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - s->dma_adc.dmasize); - } - if (s->tmpbuff) { - memset(s->tmpbuff, - (s->prop_adc. - fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - PAGE_SIZE << s->buforder_tmpbuff); - } - - va = virt_to_bus(s->dma_adc.rawbuf); - - count = s->dma_adc.dmasize; - - if (s->prop_adc. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) - count /= 2; // 16-bit. - - if (s->prop_adc.channels > 1) - count /= 2; // Assume stereo. - - CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO - "cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n", - count, (unsigned) va)); - - writel(va, s->pBA0 + BA0_DBA1); // Set buffer start address. - writel(count - 1, s->pBA0 + BA0_DBC1); // Set count. - s->dma_adc.ready = 1; - return 0; -} - - -static int prog_dmabuf_dac(struct cs4281_state *s) -{ - unsigned long va; - unsigned count; - int c; - stop_dac(s); - s->dma_dac.type = CS_TYPE_DAC; - if ((c = prog_dmabuf(s, &s->dma_dac))) - return c; - memset(s->dma_dac.rawbuf, - (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - s->dma_dac.dmasize); - - va = virt_to_bus(s->dma_dac.rawbuf); - - count = s->dma_dac.dmasize; - if (s->prop_dac. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) - count /= 2; // 16-bit. - - if (s->prop_dac.channels > 1) - count /= 2; // Assume stereo. - - writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. - writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. - - CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO - "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", - count, (unsigned) va)); - - s->dma_dac.ready = 1; - return 0; -} - - -static void clear_advance(void *buf, unsigned bsize, unsigned bptr, - unsigned len, unsigned char c) -{ - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(((char *) buf) + bptr, c, x); - bptr = 0; - len -= x; - } - CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO - "cs4281: clear_advance(): memset %d at %p for %d size \n", - (unsigned)c, ((char *) buf) + bptr, len)); - memset(((char *) buf) + bptr, c, len); -} - - - -// call with spinlock held! -static void cs4281_update_ptr(struct cs4281_state *s, int intflag) -{ - int diff; - unsigned hwptr, va; - - // update ADC pointer - if (s->ena & FMODE_READ) { - hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. - va = virt_to_bus(s->dma_adc.rawbuf); - hwptr -= (unsigned) va; - diff = - (s->dma_adc.dmasize + hwptr - - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count > s->dma_adc.dmasize) - s->dma_adc.count = s->dma_adc.dmasize; - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= - (signed) s->dma_adc.fragsize) wake_up(&s-> - dma_adc. - wait); - } else { - if (s->dma_adc.count > 0) - wake_up(&s->dma_adc.wait); - } - CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", - s, s->dma_adc.hwptr, s->dma_adc.total_bytes, s->dma_adc.count)); - } - // update DAC pointer - // - // check for end of buffer, means that we are going to wait for another interrupt - // to allow silence to fill the fifos on the part, to keep pops down to a minimum. - // - if (s->ena & FMODE_WRITE) { - hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. - va = virt_to_bus(s->dma_dac.rawbuf); - hwptr -= (unsigned) va; - diff = (s->dma_dac.dmasize + hwptr - - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= s->dma_dac.fragsize) { - s->dma_dac.wakeup = 1; - wake_up(&s->dma_dac.wait); - if (s->dma_dac.count > s->dma_dac.dmasize) - s->dma_dac.count &= - s->dma_dac.dmasize - 1; - } - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - // - // fill with silence, and do not shut down the DAC. - // Continue to play silence until the _release. - // - CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): memset %d at %p for %d size \n", - (unsigned)(s->prop_dac.fmt & - (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - s->dma_dac.rawbuf, s->dma_dac.dmasize)); - memset(s->dma_dac.rawbuf, - (s->prop_dac. - fmt & (AFMT_U8 | AFMT_U16_LE)) ? - 0x80 : 0, s->dma_dac.dmasize); - if (s->dma_dac.count < 0) { - s->dma_dac.underrun = 1; - s->dma_dac.count = 0; - CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): underrun\n")); - } - } else if (s->dma_dac.count <= - (signed) s->dma_dac.fragsize - && !s->dma_dac.endcleared) { - clear_advance(s->dma_dac.rawbuf, - s->dma_dac.dmasize, - s->dma_dac.swptr, - s->dma_dac.fragsize, - (s->prop_dac. - fmt & (AFMT_U8 | - AFMT_U16_LE)) ? 0x80 - : 0); - s->dma_dac.endcleared = 1; - } - if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) || - intflag) - { - wake_up(&s->dma_dac.wait); - } - } - CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", - s, s->dma_dac.hwptr, s->dma_dac.total_bytes, s->dma_dac.count)); - } -} - - -// --------------------------------------------------------------------- - -static void prog_codec(struct cs4281_state *s, unsigned type) -{ - unsigned long flags; - unsigned temp1, format; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_codec()+ \n")); - - spin_lock_irqsave(&s->lock, flags); - if (type == CS_TYPE_ADC) { - temp1 = readl(s->pBA0 + BA0_DCR1); - writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. - - // program sampling rates - // Note, for CS4281, capture & play rates can be set independently. - cs4281_record_rate(s, s->prop_adc.rate); - - // program ADC parameters - format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; - if (s->prop_adc. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit - if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? - format |= DMRn_BEND; - if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) - format |= DMRn_USIGN; // Unsigned. - } else - format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned - if (s->prop_adc.channels < 2) - format |= DMRn_MONO; - - writel(format, s->pBA0 + BA0_DMR1); - - CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO - "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", - (format & DMRn_SIZE8) ? "8" : "16", - (format & DMRn_USIGN) ? "Unsigned" : "Signed", - (format & DMRn_MONO) ? "Mono" : "Stereo", - s->prop_adc.rate, format)); - - s->ena &= ~FMODE_READ; // not capturing data yet - } - - - if (type == CS_TYPE_DAC) { - temp1 = readl(s->pBA0 + BA0_DCR0); - writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. - - // program sampling rates - // Note, for CS4281, capture & play rates can be set independently. - cs4281_play_rate(s, s->prop_dac.rate); - - // program DAC parameters - format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; - if (s->prop_dac. - fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit - if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) - format |= DMRn_BEND; // Big Endian. - if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) - format |= DMRn_USIGN; // Unsigned. - } else - format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned - - if (s->prop_dac.channels < 2) - format |= DMRn_MONO; - - writel(format, s->pBA0 + BA0_DMR0); - - - CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO - "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", - (format & DMRn_SIZE8) ? "8" : "16", - (format & DMRn_USIGN) ? "Unsigned" : "Signed", - (format & DMRn_MONO) ? "Mono" : "Stereo", - s->prop_dac.rate, format)); - - s->ena &= ~FMODE_WRITE; // not capturing data yet - - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: prog_codec()- \n")); -} - - -static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd, - unsigned long arg) -{ - // Index to mixer_src[] is value of AC97 Input Mux Select Reg. - // Value of array member is recording source Device ID Mask. - static const unsigned int mixer_src[8] = { - SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, - SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 - }; - void __user *argp = (void __user *)arg; - - // Index of mixtable1[] member is Device ID - // and must be <= SOUND_MIXER_NRDEVICES. - // Value of array member is index into s->mix.vol[] - static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 1, // voice - [SOUND_MIXER_LINE1] = 2, // AUX - [SOUND_MIXER_CD] = 3, // CD - [SOUND_MIXER_LINE] = 4, // Line - [SOUND_MIXER_SYNTH] = 5, // FM - [SOUND_MIXER_MIC] = 6, // Mic - [SOUND_MIXER_SPEAKER] = 7, // Speaker - [SOUND_MIXER_RECLEV] = 8, // Recording level - [SOUND_MIXER_VOLUME] = 9 // Master Volume - }; - - - static const unsigned mixreg[] = { - BA0_AC97_PCM_OUT_VOLUME, - BA0_AC97_AUX_VOLUME, - BA0_AC97_CD_VOLUME, - BA0_AC97_LINE_IN_VOLUME - }; - unsigned char l, r, rl, rr, vidx; - unsigned char attentbl[11] = - { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; - unsigned temp1; - int i, val; - - VALIDATE_STATE(s); - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: mixer_ioctl(): s=%p cmd=0x%.8x\n", s, cmd)); -#if CSDEBUG - cs_printioctl(cmd); -#endif -#if CSDEBUG_INTERFACE - - if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || - (cmd == SOUND_MIXER_CS_SETDBGMASK) || - (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || - (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || - (cmd == SOUND_MIXER_CS_APM)) - { - switch (cmd) { - - case SOUND_MIXER_CS_GETDBGMASK: - return put_user(cs_debugmask, - (unsigned long __user *) argp); - - case SOUND_MIXER_CS_GETDBGLEVEL: - return put_user(cs_debuglevel, - (unsigned long __user *) argp); - - case SOUND_MIXER_CS_SETDBGMASK: - if (get_user(val, (unsigned long __user *) argp)) - return -EFAULT; - cs_debugmask = val; - return 0; - - case SOUND_MIXER_CS_SETDBGLEVEL: - if (get_user(val, (unsigned long __user *) argp)) - return -EFAULT; - cs_debuglevel = val; - return 0; -#ifndef NOT_CS4281_PM - case SOUND_MIXER_CS_APM: - if (get_user(val, (unsigned long __user *) argp)) - return -EFAULT; - if(val == CS_IOCTL_CMD_SUSPEND) - cs4281_suspend(s); - else if(val == CS_IOCTL_CMD_RESUME) - cs4281_resume(s); - else - { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n", - val)); - } - return 0; -#endif - default: - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); - return 0; - } - } -#endif - - if (cmd == SOUND_MIXER_PRIVATE1) { - // enable/disable/query mixer preamp - if (get_user(val, (int __user *) argp)) - return -EFAULT; - if (val != -1) { - cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); - temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); - cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); - } - cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); - val = (temp1 & 0x40) ? 1 : 0; - return put_user(val, (int __user *) argp); - } - if (cmd == SOUND_MIXER_PRIVATE2) { - // enable/disable/query spatializer - if (get_user(val, (int __user *)argp)) - return -EFAULT; - if (val != -1) { - temp1 = (val & 0x3f) >> 2; - cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); - cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, - &temp1); - cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, - temp1 | 0x2000); - } - cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); - return put_user((temp1 << 2) | 3, (int __user *)argp); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strlcpy(info.id, "CS4281", sizeof(info.id)); - strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); - info.modify_counter = s->mix.modcnt; - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strlcpy(info.id, "CS4281", sizeof(info.id)); - strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int __user *) argp); - - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - // If ioctl has only the SIOC_READ bit(bit 31) - // on, process the only-read commands. - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source - cs4281_read_ac97(s, BA0_AC97_RECORD_SELECT, &temp1); - return put_user(mixer_src[temp1&7], (int __user *)argp); - - case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | - SOUND_MASK_CD | SOUND_MASK_LINE | - SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | - SOUND_MASK_RECLEV | - SOUND_MASK_SPEAKER, (int __user *)argp); - - case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source - return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_CD | SOUND_MASK_VOLUME | - SOUND_MASK_LINE1, (int __user *) argp); - - case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | - SOUND_MASK_CD | SOUND_MASK_LINE | - SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | - SOUND_MASK_RECLEV, (int __user *)argp); - - case SOUND_MIXER_CAPS: - return put_user(SOUND_CAP_EXCL_INPUT, (int __user *)argp); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES - || !(vidx = mixtable1[i])) - return -EINVAL; - return put_user(s->mix.vol[vidx - 1], (int __user *)argp); - } - } - // If ioctl doesn't have both the SIOC_READ and - // the SIOC_WRITE bit set, return invalid. - if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) - return -EINVAL; - - // Increment the count of volume writes. - s->mix.modcnt++; - - // Isolate the command; it must be a write. - switch (_IOC_NR(cmd)) { - - case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source - if (get_user(val, (int __user *)argp)) - return -EFAULT; - i = hweight32(val); // i = # bits on in val. - if (i != 1) // One & only 1 bit must be on. - return 0; - for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { - if (val == mixer_src[i]) { - temp1 = (i << 8) | i; - cs4281_write_ac97(s, - BA0_AC97_RECORD_SELECT, - temp1); - return 0; - } - } - return 0; - - case SOUND_MIXER_VOLUME: - if (get_user(val, (int __user *)argp)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; // Max soundcard.h vol is 100. - if (l < 6) { - rl = 63; - l = 0; - } else - rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. - - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; // Max right volume is 100, too - if (r < 6) { - rr = 63; - r = 0; - } else - rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. - - if ((rl > 60) && (rr > 60)) // If both l & r are 'low', - temp1 = 0x8000; // turn on the mute bit. - else - temp1 = 0; - - temp1 |= (rl << 8) | rr; - - cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1); - cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[8] = ((unsigned int) r << 8) | l; -#else - s->mix.vol[8] = val; -#endif - return put_user(s->mix.vol[8], (int __user *)argp); - - case SOUND_MIXER_SPEAKER: - if (get_user(val, (int __user *)argp)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (l < 3) { - rl = 0; - l = 0; - } else { - rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. - l = (rl * 13 + 5) / 2; - } - - if (rl < 3) { - temp1 = 0x8000; - rl = 0; - } else - temp1 = 0; - rl = 15 - rl; // Convert volume to attenuation. - temp1 |= rl << 1; - cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[6] = l << 8; -#else - s->mix.vol[6] = val; -#endif - return put_user(s->mix.vol[6], (int __user *)argp); - - case SOUND_MIXER_RECLEV: - if (get_user(val, (int __user *)argp)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. - rr = (r * 2 - 5) / 13; - if (rl < 3 && rr < 3) - temp1 = 0x8000; - else - temp1 = 0; - - temp1 = temp1 | (rl << 8) | rr; - cs4281_write_ac97(s, BA0_AC97_RECORD_GAIN, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[7] = ((unsigned int) r << 8) | l; -#else - s->mix.vol[7] = val; -#endif - return put_user(s->mix.vol[7], (int __user *)argp); - - case SOUND_MIXER_MIC: - if (get_user(val, (int __user *)argp)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (l < 1) { - l = 0; - rl = 0; - } else { - rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. - l = (rl * 16 + 4) / 5; - } - cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); - temp1 &= 0x40; // Isolate 20db gain bit. - if (rl < 3) { - temp1 |= 0x8000; - rl = 0; - } - rl = 31 - rl; // Convert volume to attenuation. - temp1 |= rl; - cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[5] = val << 8; -#else - s->mix.vol[5] = val; -#endif - return put_user(s->mix.vol[5], (int __user *)argp); - - - case SOUND_MIXER_SYNTH: - if (get_user(val, (int __user *)argp)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (get_user(val, (int __user *)argp)) - return -EFAULT; - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. - rr = (r * 2 - 11) / 3; - if (rl < 3) // If l is low, turn on - temp1 = 0x0080; // the mute bit. - else - temp1 = 0; - - rl = 63 - rl; // Convert vol to attenuation. - writel(temp1 | rl, s->pBA0 + BA0_FMLVC); - if (rr < 3) // If rr is low, turn on - temp1 = 0x0080; // the mute bit. - else - temp1 = 0; - rr = 63 - rr; // Convert vol to attenuation. - writel(temp1 | rr, s->pBA0 + BA0_FMRVC); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[4] = (r << 8) | l; -#else - s->mix.vol[4] = val; -#endif - return put_user(s->mix.vol[4], (int __user *)argp); - - - default: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4281: mixer_ioctl(): default\n")); - - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) - return -EINVAL; - if (get_user(val, (int __user *)argp)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (l < 1) { - l = 0; - rl = 31; - } else - rl = (attentbl[(l * 10) / 100]) >> 1; - - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - if (r < 1) { - r = 0; - rr = 31; - } else - rr = (attentbl[(r * 10) / 100]) >> 1; - if ((rl > 30) && (rr > 30)) - temp1 = 0x8000; - else - temp1 = 0; - temp1 = temp1 | (rl << 8) | rr; - cs4281_write_ac97(s, mixreg[vidx - 1], temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; -#else - s->mix.vol[vidx - 1] = val; -#endif -#ifndef NOT_CS4281_PM - CS_DBGOUT(CS_PM, 9, printk(KERN_INFO - "write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", - vidx-1,temp1,s->mix.vol[vidx-1])); -#endif - return put_user(s->mix.vol[vidx - 1], (int __user *)argp); - } -} - - -// --------------------------------------------------------------------- - -static int cs4281_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct cs4281_state *s=NULL; - struct list_head *entry; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n")); - - list_for_each(entry, &cs4281_devs) - { - s = list_entry(entry, struct cs4281_state, list); - if(s->dev_mixer == minor) - break; - } - if (!s) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, - printk(KERN_INFO "cs4281: cs4281_open_mixdev()- -ENODEV\n")); - return -ENODEV; - } - VALIDATE_STATE(s); - file->private_data = s; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_open_mixdev()- 0\n")); - - return nonseekable_open(inode, file); -} - - -static int cs4281_release_mixdev(struct inode *inode, struct file *file) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - - VALIDATE_STATE(s); - return 0; -} - - -static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct cs4281_state *) file->private_data, cmd, - arg); -} - - -// ****************************************************************************************** -// Mixer file operations struct. -// ****************************************************************************************** -static /*const */ struct file_operations cs4281_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = cs4281_ioctl_mixdev, - .open = cs4281_open_mixdev, - .release = cs4281_release_mixdev, -}; - -// --------------------------------------------------------------------- - - -static int drain_adc(struct cs4281_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - unsigned tmo; - - if (s->dma_adc.mapped) - return 0; - add_wait_queue(&s->dma_adc.wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_adc.count; - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: drain_adc() %d\n", count)); - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) { - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: drain_adc() count<0\n")); - break; - } - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_adc.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = - 3 * HZ * (count + - s->dma_adc.fragsize) / 2 / s->prop_adc.rate; - if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - tmo >>= 1; - if (s->prop_adc.channels > 1) - tmo >>= 1; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "cs4281: dma timed out??\n"); - } - remove_wait_queue(&s->dma_adc.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static int drain_dac(struct cs4281_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - unsigned tmo; - - if (s->dma_dac.mapped) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = - 3 * HZ * (count + - s->dma_dac.fragsize) / 2 / s->prop_dac.rate; - if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) - tmo >>= 1; - if (s->prop_dac.channels > 1) - tmo >>= 1; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "cs4281: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -//**************************************************************************** -// -// CopySamples copies 16-bit stereo samples from the source to the -// destination, possibly converting down to either 8-bit or mono or both. -// count specifies the number of output bytes to write. -// -// Arguments: -// -// dst - Pointer to a destination buffer. -// src - Pointer to a source buffer -// count - The number of bytes to copy into the destination buffer. -// iChannels - Stereo - 2 -// Mono - 1 -// fmt - AFMT_xxx (soundcard.h formats) -// -// NOTES: only call this routine for conversion to 8bit from 16bit -// -//**************************************************************************** -static void CopySamples(char *dst, char *src, int count, int iChannels, - unsigned fmt) -{ - - unsigned short *psSrc; - long lAudioSample; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: CopySamples()+ ")); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " dst=%p src=%p count=%d iChannels=%d fmt=0x%x\n", - dst, src, (unsigned) count, (unsigned) iChannels, (unsigned) fmt)); - - // Gershwin does format conversion in hardware so normally - // we don't do any host based coversion. The data formatter - // truncates 16 bit data to 8 bit and that causes some hiss. - // We have already forced the HW to do 16 bit sampling and - // 2 channel so that we can use software to round instead - // of truncate - - // - // See if the data should be output as 8-bit unsigned stereo. - // or if the data should be output at 8-bit unsigned mono. - // - if ( ((iChannels == 2) && (fmt & AFMT_U8)) || - ((iChannels == 1) && (fmt & AFMT_U8)) ) { - // - // Convert each 16-bit unsigned stereo sample to 8-bit unsigned - // stereo using rounding. - // - psSrc = (unsigned short *) src; - count = count / 2; - while (count--) { - lAudioSample = (long) psSrc[count] + (long) 0x80; - if (lAudioSample > 0xffff) { - lAudioSample = 0xffff; - } - dst[count] = (char) (lAudioSample >> 8); - } - } - // - // check for 8-bit signed stereo. - // - else if ((iChannels == 2) && (fmt & AFMT_S8)) { - // - // Convert each 16-bit stereo sample to 8-bit stereo using rounding. - // - psSrc = (short *) src; - while (count--) { - lAudioSample = - (((long) psSrc[0] + (long) psSrc[1]) / 2); - psSrc += 2; - *dst++ = (char) ((short) lAudioSample >> 8); - } - } - // - // Otherwise, the data should be output as 8-bit signed mono. - // - else if ((iChannels == 1) && (fmt & AFMT_S8)) { - // - // Convert each 16-bit signed mono sample to 8-bit signed mono - // using rounding. - // - psSrc = (short *) src; - count = count / 2; - while (count--) { - lAudioSample = - (((long) psSrc[0] + (long) psSrc[1]) / 2); - if (lAudioSample > 0x7fff) { - lAudioSample = 0x7fff; - } - psSrc += 2; - *dst++ = (char) ((short) lAudioSample >> 8); - } - } -} - -// -// cs_copy_to_user() -// replacement for the standard copy_to_user, to allow for a conversion from -// 16 bit to 8 bit if the record conversion is active. the cs4281 has some -// issues with 8 bit capture, so the driver always captures data in 16 bit -// and then if the user requested 8 bit, converts from 16 to 8 bit. -// -static unsigned cs_copy_to_user(struct cs4281_state *s, void __user *dest, - unsigned *hwsrc, unsigned cnt, - unsigned *copied) -{ - void *src = hwsrc; //default to the standard destination buffer addr - - CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO - "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=%p\n", - s->prop_adc.fmt, s->prop_adc.fmt_original, - (unsigned) cnt, dest)); - - if (cnt > s->dma_adc.dmasize) { - cnt = s->dma_adc.dmasize; - } - if (!cnt) { - *copied = 0; - return 0; - } - if (s->conversion) { - if (!s->tmpbuff) { - *copied = cnt / 2; - return 0; - } - CopySamples(s->tmpbuff, (void *) hwsrc, cnt, - (unsigned) s->prop_adc.channels, - s->prop_adc.fmt_original); - src = s->tmpbuff; - cnt = cnt / 2; - } - - if (copy_to_user(dest, src, cnt)) { - *copied = 0; - return -EFAULT; - } - *copied = cnt; - CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt)); - return 0; -} - -// --------------------------------------------------------------------- - -static ssize_t cs4281_read(struct file *file, char __user *buffer, size_t count, - loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - unsigned copied = 0; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_read()+ %Zu \n", count)); - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; -// -// "count" is the amount of bytes to read (from app), is decremented each loop -// by the amount of bytes that have been returned to the user buffer. -// "cnt" is the running total of each read from the buffer (changes each loop) -// "buffer" points to the app's buffer -// "ret" keeps a running total of the amount of bytes that have been copied -// to the user buffer. -// "copied" is the total bytes copied into the user buffer for each loop. -// - while (count > 0) { - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - "_read() count>0 count=%Zu .count=%d .swptr=%d .hwptr=%d \n", - count, s->dma_adc.count, - s->dma_adc.swptr, s->dma_adc.hwptr)); - spin_lock_irqsave(&s->lock, flags); - - // get the current copy point of the sw buffer - swptr = s->dma_adc.swptr; - - // cnt is the amount of unread bytes from the end of the - // hw buffer to the current sw pointer - cnt = s->dma_adc.dmasize - swptr; - - // dma_adc.count is the current total bytes that have not been read. - // if the amount of unread bytes from the current sw pointer to the - // end of the buffer is greater than the current total bytes that - // have not been read, then set the "cnt" (unread bytes) to the - // amount of unread bytes. - - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - spin_unlock_irqrestore(&s->lock, flags); - // - // if we are converting from 8/16 then we need to copy - // twice the number of 16 bit bytes then 8 bit bytes. - // - if (s->conversion) { - if (cnt > (count * 2)) - cnt = (count * 2); - } else { - if (cnt > count) - cnt = count; - } - // - // "cnt" NOW is the smaller of the amount that will be read, - // and the amount that is requested in this read (or partial). - // if there are no bytes in the buffer to read, then start the - // ADC and wait for the interrupt handler to wake us up. - // - if (cnt <= 0) { - - // start up the dma engine and then continue back to the top of - // the loop when wake up occurs. - start_adc(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_adc.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - // there are bytes in the buffer to read. - // copy from the hw buffer over to the user buffer. - // user buffer is designated by "buffer" - // virtual address to copy from is rawbuf+swptr - // the "cnt" is the number of bytes to read. - - CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO - "_read() copy_to cnt=%d count=%Zu ", cnt, count)); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .dmasize=%d .count=%d buffer=%p ret=%Zd\n", - s->dma_adc.dmasize, s->dma_adc.count, buffer, ret)); - - if (cs_copy_to_user - (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= copied; - buffer += copied; - ret += copied; - start_adc(s); - } - CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_read()- %Zd\n", ret)); - return ret; -} - - -static ssize_t cs4281_write(struct file *file, const char __user *buffer, - size_t count, loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr, hwptr, busaddr; - int cnt; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_write()+ count=%Zu\n", - count)); - VALIDATE_STATE(s); - - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - if (s->dma_dac.underrun) { - s->dma_dac.underrun = 0; - hwptr = readl(s->pBA0 + BA0_DCA0); - busaddr = virt_to_bus(s->dma_dac.rawbuf); - hwptr -= (unsigned) busaddr; - s->dma_dac.swptr = s->dma_dac.hwptr = hwptr; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize - swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->dma_dac.wait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_dac.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(s); - } - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_write()- %Zd\n", ret)); - return ret; -} - - -static unsigned int cs4281_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - unsigned long flags; - unsigned int mask = 0; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO "cs4281: cs4281_poll()+\n")); - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO - "cs4281: cs4281_poll() wait on FMODE_WRITE\n")); - if(!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO - "cs4281: cs4281_poll() wait on FMODE_READ\n")); - if(!s->dma_dac.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= - (signed) s->dma_dac.fragsize) { - if (s->dma_dac.wakeup) - mask |= POLLOUT | POLLWRNORM; - else - mask = 0; - s->dma_dac.wakeup = 0; - } - } else { - if ((signed) (s->dma_dac.dmasize/2) >= s->dma_dac.count) - mask |= POLLOUT | POLLWRNORM; - } - } else if (file->f_mode & FMODE_READ) { - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } else { - if (s->dma_adc.count > 0) - mask |= POLLIN | POLLRDNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO "cs4281: cs4281_poll()- 0x%.8x\n", - mask)); - return mask; -} - - -static int cs4281_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - struct dmabuf *db; - int ret; - unsigned long size; - - CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_mmap()+\n")); - - VALIDATE_STATE(s); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac(s)) != 0) - return ret; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) - return ret; - db = &s->dma_adc; - } else - return -EINVAL; -// -// only support PLAYBACK for now -// - db = &s->dma_dac; - - if (cs4x_pgoff(vma) != 0) - return -EINVAL; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - return -EINVAL; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - return -EAGAIN; - db->mapped = 1; - - CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, - printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n", - (unsigned) size)); - - return 0; -} - - -static int cs4281_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - int __user *p = (int __user *)arg; - - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): file=%p cmd=0x%.8x\n", file, cmd)); -#if CSDEBUG - cs_printioctl(cmd); -#endif - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): SOUND_VERSION=0x%.8x\n", - SOUND_VERSION)); - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_SYNC\n")); - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, - 0 /*file->f_flags & O_NONBLOCK */ - ); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | - DSP_CAP_TRIGGER | DSP_CAP_MMAP, - p); - - case SNDCTL_DSP_RESET: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_RESET\n")); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.swptr = s->dma_dac.hwptr = - s->dma_dac.count = s->dma_dac.total_bytes = - s->dma_dac.blocks = s->dma_dac.wakeup = 0; - prog_codec(s, CS_TYPE_DAC); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = - s->dma_adc.count = s->dma_adc.total_bytes = - s->dma_adc.blocks = s->dma_dac.wakeup = 0; - prog_codec(s, CS_TYPE_ADC); - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val)); - // - // support independent capture and playback channels - // assume that the file mode bit determines the - // direction of the data flow. - // - if (file->f_mode & FMODE_READ) { - if (val >= 0) { - stop_adc(s); - s->dma_adc.ready = 0; - // program sampling rates - if (val > 48000) - val = 48000; - if (val < 6300) - val = 6300; - s->prop_adc.rate = val; - prog_codec(s, CS_TYPE_ADC); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val >= 0) { - stop_dac(s); - s->dma_dac.ready = 0; - // program sampling rates - if (val > 48000) - val = 48000; - if (val < 6300) - val = 6300; - s->prop_dac.rate = val; - prog_codec(s, CS_TYPE_DAC); - } - } - - if (file->f_mode & FMODE_WRITE) - val = s->prop_dac.rate; - else if (file->f_mode & FMODE_READ) - val = s->prop_adc.rate; - - return put_user(val, p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_STEREO val=%d\n", val)); - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - s->prop_adc.channels = val ? 2 : 1; - prog_codec(s, CS_TYPE_ADC); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - s->prop_dac.channels = val ? 2 : 1; - prog_codec(s, CS_TYPE_DAC); - } - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_CHANNELS val=%d\n", - val)); - if (val != 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - s->prop_adc.channels = 2; - else - s->prop_adc.channels = 1; - prog_codec(s, CS_TYPE_ADC); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - s->prop_dac.channels = 2; - else - s->prop_dac.channels = 1; - prog_codec(s, CS_TYPE_DAC); - } - } - - if (file->f_mode & FMODE_WRITE) - val = s->prop_dac.channels; - else if (file->f_mode & FMODE_READ) - val = s->prop_adc.channels; - - return put_user(val, p); - - case SNDCTL_DSP_GETFMTS: // Returns a mask - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_GETFMT val=0x%.8x\n", - AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | - AFMT_U8)); - return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | - AFMT_U8, p); - - case SNDCTL_DSP_SETFMT: - if (get_user(val, p)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_SETFMT val=0x%.8x\n", - val)); - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val != AFMT_S16_LE - && val != AFMT_U16_LE && val != AFMT_S8 - && val != AFMT_U8) - val = AFMT_U8; - s->prop_adc.fmt = val; - s->prop_adc.fmt_original = s->prop_adc.fmt; - prog_codec(s, CS_TYPE_ADC); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val != AFMT_S16_LE - && val != AFMT_U16_LE && val != AFMT_S8 - && val != AFMT_U8) - val = AFMT_U8; - s->prop_dac.fmt = val; - s->prop_dac.fmt_original = s->prop_dac.fmt; - prog_codec(s, CS_TYPE_DAC); - } - } else { - if (file->f_mode & FMODE_WRITE) - val = s->prop_dac.fmt_original; - else if (file->f_mode & FMODE_READ) - val = s->prop_adc.fmt_original; - } - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_SETFMT return val=0x%.8x\n", - val)); - return put_user(val, p); - - case SNDCTL_DSP_POST: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): DSP_POST\n")); - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & s->ena & FMODE_READ) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & s->ena & FMODE_WRITE) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready - && (ret = prog_dmabuf_adc(s))) - return ret; - start_adc(s); - } else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready - && (ret = prog_dmabuf_dac(s))) - return ret; - start_dac(s); - } else - stop_dac(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) - return val; - spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); - abinfo.fragsize = s->dma_dac.fragsize; - if (s->dma_dac.mapped) - abinfo.bytes = s->dma_dac.dmasize; - else - abinfo.bytes = - s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", - abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, - abinfo.fragments)); - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(p, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) - return val; - spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); - if (s->conversion) { - abinfo.fragsize = s->dma_adc.fragsize / 2; - abinfo.bytes = s->dma_adc.count / 2; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = - abinfo.bytes >> (s->dma_adc.fragshift - 1); - } else { - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = - abinfo.bytes >> s->dma_adc.fragshift; - } - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(p, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if(!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); - val = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if(!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); - cinfo.bytes = s->dma_adc.total_bytes; - if (s->dma_adc.mapped) { - cinfo.blocks = - (cinfo.bytes >> s->dma_adc.fragshift) - - s->dma_adc.blocks; - s->dma_adc.blocks = - cinfo.bytes >> s->dma_adc.fragshift; - } else { - if (s->conversion) { - cinfo.blocks = - s->dma_adc.count / - 2 >> (s->dma_adc.fragshift - 1); - } else - cinfo.blocks = - s->dma_adc.count >> s->dma_adc. - fragshift; - } - if (s->conversion) - cinfo.ptr = s->dma_adc.hwptr / 2; - else - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize - 1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(p, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if(!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); - cinfo.bytes = s->dma_dac.total_bytes; - if (s->dma_dac.mapped) { - cinfo.blocks = - (cinfo.bytes >> s->dma_dac.fragshift) - - s->dma_dac.blocks; - s->dma_dac.blocks = - cinfo.bytes >> s->dma_dac.fragshift; - } else { - cinfo.blocks = - s->dma_dac.count >> s->dma_dac.fragshift; - } - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize - 1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(p, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf_dac(s))) - return val; - return put_user(s->dma_dac.fragsize, p); - } - if ((val = prog_dmabuf_adc(s))) - return val; - if (s->conversion) - return put_user(s->dma_adc.fragsize / 2, p); - else - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - return 0; // Say OK, but do nothing. - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) - || (file->f_mode & FMODE_WRITE - && s->dma_dac.subdivision)) return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - else if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - if (file->f_mode & FMODE_READ) - return put_user(s->prop_adc.rate, p); - else if (file->f_mode & FMODE_WRITE) - return put_user(s->prop_dac.rate, p); - - case SOUND_PCM_READ_CHANNELS: - if (file->f_mode & FMODE_READ) - return put_user(s->prop_adc.channels, p); - else if (file->f_mode & FMODE_WRITE) - return put_user(s->prop_dac.channels, p); - - case SOUND_PCM_READ_BITS: - if (file->f_mode & FMODE_READ) - return - put_user( - (s->prop_adc. - fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, - p); - else if (file->f_mode & FMODE_WRITE) - return - put_user( - (s->prop_dac. - fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, - p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } - return mixer_ioctl(s, cmd, arg); -} - - -static int cs4281_release(struct inode *inode, struct file *file) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO - "cs4281: cs4281_release(): inode=%p file=%p f_mode=%d\n", - inode, file, file->f_mode)); - - VALIDATE_STATE(s); - - if (file->f_mode & FMODE_WRITE) { - drain_dac(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_sem_dac); - stop_dac(s); - dealloc_dmabuf(s, &s->dma_dac); - s->open_mode &= ~FMODE_WRITE; - mutex_unlock(&s->open_sem_dac); - wake_up(&s->open_wait_dac); - } - if (file->f_mode & FMODE_READ) { - drain_adc(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_sem_adc); - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - s->open_mode &= ~FMODE_READ; - mutex_unlock(&s->open_sem_adc); - wake_up(&s->open_wait_adc); - } - return 0; -} - -static int cs4281_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct cs4281_state *s=NULL; - struct list_head *entry; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_open(): inode=%p file=%p f_mode=0x%x\n", - inode, file, file->f_mode)); - - list_for_each(entry, &cs4281_devs) - { - s = list_entry(entry, struct cs4281_state, list); - - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - if (entry == &cs4281_devs) - return -ENODEV; - if (!s) { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); - return -ENODEV; - } - VALIDATE_STATE(s); - file->private_data = s; - - // wait for device to become free - if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO - "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n")); - return -ENODEV; - } - if (file->f_mode & FMODE_WRITE) { - mutex_lock(&s->open_sem_dac); - while (s->open_mode & FMODE_WRITE) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_sem_dac); - return -EBUSY; - } - mutex_unlock(&s->open_sem_dac); - interruptible_sleep_on(&s->open_wait_dac); - - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_sem_dac); - } - } - if (file->f_mode & FMODE_READ) { - mutex_lock(&s->open_sem_adc); - while (s->open_mode & FMODE_READ) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_sem_adc); - return -EBUSY; - } - mutex_unlock(&s->open_sem_adc); - interruptible_sleep_on(&s->open_wait_adc); - - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_sem_adc); - } - } - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - if (file->f_mode & FMODE_READ) { - s->prop_adc.fmt = AFMT_U8; - s->prop_adc.fmt_original = s->prop_adc.fmt; - s->prop_adc.channels = 1; - s->prop_adc.rate = 8000; - s->prop_adc.clkdiv = 96 | 0x80; - s->conversion = 0; - s->ena &= ~FMODE_READ; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = - s->dma_adc.subdivision = 0; - mutex_unlock(&s->open_sem_adc); - - if (prog_dmabuf_adc(s)) { - CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR - "cs4281: adc Program dmabufs failed.\n")); - cs4281_release(inode, file); - return -ENOMEM; - } - prog_codec(s, CS_TYPE_ADC); - } - if (file->f_mode & FMODE_WRITE) { - s->prop_dac.fmt = AFMT_U8; - s->prop_dac.fmt_original = s->prop_dac.fmt; - s->prop_dac.channels = 1; - s->prop_dac.rate = 8000; - s->prop_dac.clkdiv = 96 | 0x80; - s->conversion = 0; - s->ena &= ~FMODE_WRITE; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = - s->dma_dac.subdivision = 0; - mutex_unlock(&s->open_sem_dac); - - if (prog_dmabuf_dac(s)) { - CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR - "cs4281: dac Program dmabufs failed.\n")); - cs4281_release(inode, file); - return -ENOMEM; - } - prog_codec(s, CS_TYPE_DAC); - } - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, - printk(KERN_INFO "cs4281: cs4281_open()- 0\n")); - return nonseekable_open(inode, file); -} - - -// ****************************************************************************************** -// Wave (audio) file operations struct. -// ****************************************************************************************** -static /*const */ struct file_operations cs4281_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = cs4281_read, - .write = cs4281_write, - .poll = cs4281_poll, - .ioctl = cs4281_ioctl, - .mmap = cs4281_mmap, - .open = cs4281_open, - .release = cs4281_release, -}; - -// --------------------------------------------------------------------- - -// hold spinlock for the following! -static void cs4281_handle_midi(struct cs4281_state *s) -{ - unsigned char ch; - int wake; - unsigned temp1; - - wake = 0; - while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) { - ch = readl(s->pBA0 + BA0_MIDRP); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) { - temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff; - writel(temp1, s->pBA0 + BA0_MIDWP); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF - 16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} - - - -static irqreturn_t cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cs4281_state *s = (struct cs4281_state *) dev_id; - unsigned int temp1; - - // fastpath out, to ease interrupt sharing - temp1 = readl(s->pBA0 + BA0_HISR); // Get Int Status reg. - - CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO - "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1)); -/* -* If not DMA or MIDI interrupt, then just return. -*/ - if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) { - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); - CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO - "cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n")); - return IRQ_NONE; - } - - if (temp1 & HISR_DMA0) // If play interrupt, - readl(s->pBA0 + BA0_HDSR0); // clear the source. - - if (temp1 & HISR_DMA1) // Same for play. - readl(s->pBA0 + BA0_HDSR1); - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Local EOI - - spin_lock(&s->lock); - cs4281_update_ptr(s,CS_TRUE); - cs4281_handle_midi(s); - spin_unlock(&s->lock); - return IRQ_HANDLED; -} - -// ************************************************************************** - -static void cs4281_midi_timer(unsigned long data) -{ - struct cs4281_state *s = (struct cs4281_state *) data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - cs4281_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies + 1; - add_timer(&s->midi.timer); -} - - -// --------------------------------------------------------------------- - -static ssize_t cs4281_midi_read(struct file *file, char __user *buffer, - size_t count, loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - } - return ret; -} - - -static ssize_t cs4281_midi_write(struct file *file, const char __user *buffer, - size_t count, loff_t * ppos) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) - cs4281_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - cs4281_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - return ret; -} - - -static unsigned int cs4281_midi_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_flags & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_flags & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_flags & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_flags & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - - -static int cs4281_midi_open(struct inode *inode, struct file *file) -{ - unsigned long flags, temp1; - unsigned int minor = iminor(inode); - struct cs4281_state *s=NULL; - struct list_head *entry; - list_for_each(entry, &cs4281_devs) - { - s = list_entry(entry, struct cs4281_state, list); - - if (s->dev_midi == minor) - break; - } - - if (entry == &cs4281_devs) - return -ENODEV; - if (!s) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); - return -ENODEV; - } - VALIDATE_STATE(s); - file->private_data = s; - // wait for device to become free - mutex_lock(&s->open_sem); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_sem); - return -EBUSY; - } - mutex_unlock(&s->open_sem); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_sem); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - writel(1, s->pBA0 + BA0_MIDCR); // Reset the interface. - writel(0, s->pBA0 + BA0_MIDCR); // Return to normal mode. - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - writel(0x0000000f, s->pBA0 + BA0_MIDCR); // Enable transmit, record, ints. - temp1 = readl(s->pBA0 + BA0_HIMR); - writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR); // Enable midi int. recognition. - writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies + 1; - s->midi.timer.data = (unsigned long) s; - s->midi.timer.function = cs4281_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= - (file-> - f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | - FMODE_MIDI_WRITE); - mutex_unlock(&s->open_sem); - return nonseekable_open(inode, file); -} - - -static int cs4281_midi_release(struct inode *inode, struct file *file) -{ - struct cs4281_state *s = - (struct cs4281_state *) file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG - "cs4281: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - current->state = TASK_RUNNING; - } - mutex_lock(&s->open_sem); - s->open_mode &= - (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | - FMODE_MIDI_WRITE); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - writel(0, s->pBA0 + BA0_MIDCR); // Disable Midi interrupts. - del_timer(&s->midi.timer); - } - spin_unlock_irqrestore(&s->lock, flags); - mutex_unlock(&s->open_sem); - wake_up(&s->open_wait); - return 0; -} - -// ****************************************************************************************** -// Midi file operations struct. -// ****************************************************************************************** -static /*const */ struct file_operations cs4281_midi_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = cs4281_midi_read, - .write = cs4281_midi_write, - .poll = cs4281_midi_poll, - .open = cs4281_midi_open, - .release = cs4281_midi_release, -}; - - -// --------------------------------------------------------------------- - -// maximum number of devices -#define NR_DEVICE 8 // Only eight devices supported currently. - -// --------------------------------------------------------------------- - -static struct initvol { - int mixch; - int vol; -} initvol[] __devinitdata = { - - { - SOUND_MIXER_WRITE_VOLUME, 0x4040}, { - SOUND_MIXER_WRITE_PCM, 0x4040}, { - SOUND_MIXER_WRITE_SYNTH, 0x4040}, { - SOUND_MIXER_WRITE_CD, 0x4040}, { - SOUND_MIXER_WRITE_LINE, 0x4040}, { - SOUND_MIXER_WRITE_LINE1, 0x4040}, { - SOUND_MIXER_WRITE_RECLEV, 0x0000}, { - SOUND_MIXER_WRITE_SPEAKER, 0x4040}, { - SOUND_MIXER_WRITE_MIC, 0x0000} -}; - - -#ifndef NOT_CS4281_PM -static void __devinit cs4281_BuildFIFO( - struct cs4281_pipeline *p, - struct cs4281_state *s) -{ - switch(p->number) - { - case 0: /* playback */ - { - p->u32FCRnAddress = BA0_FCR0; - p->u32FSICnAddress = BA0_FSIC0; - p->u32FPDRnAddress = BA0_FPDR0; - break; - } - case 1: /* capture */ - { - p->u32FCRnAddress = BA0_FCR1; - p->u32FSICnAddress = BA0_FSIC1; - p->u32FPDRnAddress = BA0_FPDR1; - break; - } - - case 2: - { - p->u32FCRnAddress = BA0_FCR2; - p->u32FSICnAddress = BA0_FSIC2; - p->u32FPDRnAddress = BA0_FPDR2; - break; - } - case 3: - { - p->u32FCRnAddress = BA0_FCR3; - p->u32FSICnAddress = BA0_FSIC3; - p->u32FPDRnAddress = BA0_FPDR3; - break; - } - default: - break; - } - // - // first read the hardware to initialize the member variables - // - p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress); - p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress); - p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress); - -} - -static void __devinit cs4281_BuildDMAengine( - struct cs4281_pipeline *p, - struct cs4281_state *s) -{ -/* -* initialize all the addresses of this pipeline dma info. -*/ - switch(p->number) - { - case 0: /* playback */ - { - p->u32DBAnAddress = BA0_DBA0; - p->u32DCAnAddress = BA0_DCA0; - p->u32DBCnAddress = BA0_DBC0; - p->u32DCCnAddress = BA0_DCC0; - p->u32DMRnAddress = BA0_DMR0; - p->u32DCRnAddress = BA0_DCR0; - p->u32HDSRnAddress = BA0_HDSR0; - break; - } - - case 1: /* capture */ - { - p->u32DBAnAddress = BA0_DBA1; - p->u32DCAnAddress = BA0_DCA1; - p->u32DBCnAddress = BA0_DBC1; - p->u32DCCnAddress = BA0_DCC1; - p->u32DMRnAddress = BA0_DMR1; - p->u32DCRnAddress = BA0_DCR1; - p->u32HDSRnAddress = BA0_HDSR1; - break; - } - - case 2: - { - p->u32DBAnAddress = BA0_DBA2; - p->u32DCAnAddress = BA0_DCA2; - p->u32DBCnAddress = BA0_DBC2; - p->u32DCCnAddress = BA0_DCC2; - p->u32DMRnAddress = BA0_DMR2; - p->u32DCRnAddress = BA0_DCR2; - p->u32HDSRnAddress = BA0_HDSR2; - break; - } - - case 3: - { - p->u32DBAnAddress = BA0_DBA3; - p->u32DCAnAddress = BA0_DCA3; - p->u32DBCnAddress = BA0_DBC3; - p->u32DCCnAddress = BA0_DCC3; - p->u32DMRnAddress = BA0_DMR3; - p->u32DCRnAddress = BA0_DCR3; - p->u32HDSRnAddress = BA0_HDSR3; - break; - } - default: - break; - } - -// -// Initialize the dma values for this pipeline -// - p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress); - p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress); - p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress); - p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress); - -} - -static void __devinit cs4281_InitPM(struct cs4281_state *s) -{ - int i; - struct cs4281_pipeline *p; - - for(i=0;ipl[i]; - p->number = i; - cs4281_BuildDMAengine(p,s); - cs4281_BuildFIFO(p,s); - /* - * currently only 2 pipelines are used - * so, only set the valid bit on the playback and capture. - */ - if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || - (i == CS4281_CAPTURE_PIPELINE_NUMBER)) - p->flags |= CS4281_PIPELINE_VALID; - } - s->pm.u32SSPM_BITS = 0x7e; /* rev c, use 0x7c for rev a or b */ -} -#endif - -static int __devinit cs4281_probe(struct pci_dev *pcidev, - const struct pci_device_id *pciid) -{ - struct cs4281_state *s; - dma_addr_t dma_mask; - mm_segment_t fs; - int i, val; - unsigned int temp1, temp2; - - CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, - printk(KERN_INFO "cs4281: probe()+\n")); - - if (pci_enable_device(pcidev)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: pci_enable_device() failed\n")); - return -1; - } - if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) || - !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe()- Memory region not assigned\n")); - return -ENODEV; - } - if (pcidev->irq == 0) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() IRQ not assigned\n")); - return -ENODEV; - } - dma_mask = 0xffffffff; /* this enables playback and recording */ - i = pci_set_dma_mask(pcidev, dma_mask); - if (i) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n")); - return i; - } - if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() no memory for state struct.\n")); - return -1; - } - memset(s, 0, sizeof(struct cs4281_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->open_wait_adc); - init_waitqueue_head(&s->open_wait_dac); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - mutex_init(&s->open_sem); - mutex_init(&s->open_sem_adc); - mutex_init(&s->open_sem_dac); - spin_lock_init(&s->lock); - s->pBA0phys = pci_resource_start(pcidev, 0); - s->pBA1phys = pci_resource_start(pcidev, 1); - - /* Convert phys to linear. */ - s->pBA0 = ioremap_nocache(s->pBA0phys, 4096); - if (!s->pBA0) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR - "cs4281: BA0 I/O mapping failed. Skipping part.\n")); - goto err_free; - } - s->pBA1 = ioremap_nocache(s->pBA1phys, 65536); - if (!s->pBA1) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR - "cs4281: BA1 I/O mapping failed. Skipping part.\n")); - goto err_unmap; - } - - temp1 = readl(s->pBA0 + BA0_PCICFG00); - temp2 = readl(s->pBA0 + BA0_PCICFG04); - - CS_DBGOUT(CS_INIT, 2, - printk(KERN_INFO - "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=%p pBA1=%p \n", - (unsigned) temp1, (unsigned) temp2, s->pBA0, s->pBA1)); - CS_DBGOUT(CS_INIT, 2, - printk(KERN_INFO - "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", - (unsigned) s->pBA0phys, (unsigned) s->pBA1phys)); - -#ifndef NOT_CS4281_PM - s->pm.flags = CS4281_PM_IDLE; -#endif - temp1 = cs4281_hw_init(s); - if (temp1) { - CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR - "cs4281: cs4281_hw_init() failed. Skipping part.\n")); - goto err_irq; - } - s->magic = CS4281_MAGIC; - s->pcidev = pcidev; - s->irq = pcidev->irq; - if (request_irq - (s->irq, cs4281_interrupt, IRQF_SHARED, "Crystal CS4281", s)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, - printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); - goto err_irq; - } - if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) < - 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() register_sound_dsp() failed.\n")); - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) < - 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() register_sound_mixer() failed.\n")); - goto err_dev2; - } - if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4281: probe() register_sound_midi() failed.\n")); - goto err_dev3; - } -#ifndef NOT_CS4281_PM - cs4281_InitPM(s); - s->pm.flags |= CS4281_PM_NOT_REGISTERED; -#endif - - pci_set_master(pcidev); // enable bus mastering - - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); - for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); - } - val = 1; // enable mic preamp - mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val); - set_fs(fs); - - pci_set_drvdata(pcidev, s); - list_add(&s->list, &cs4281_devs); - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: probe()- device allocated successfully\n")); - return 0; - - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - free_irq(s->irq, s); - err_irq: - iounmap(s->pBA1); - err_unmap: - iounmap(s->pBA0); - err_free: - kfree(s); - - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO - "cs4281: probe()- no device allocated\n")); - return -ENODEV; -} // probe_cs4281 - - -// --------------------------------------------------------------------- - -static void __devexit cs4281_remove(struct pci_dev *pci_dev) -{ - struct cs4281_state *s = pci_get_drvdata(pci_dev); - // stop DMA controller - synchronize_irq(s->irq); - free_irq(s->irq, s); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_midi(s->dev_midi); - iounmap(s->pBA1); - iounmap(s->pBA0); - pci_set_drvdata(pci_dev,NULL); - list_del(&s->list); - kfree(s); - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs4281_remove()-: remove successful\n")); -} - -static struct pci_device_id cs4281_pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_CIRRUS, - .device = PCI_DEVICE_ID_CRYSTAL_CS4281, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl); - -static struct pci_driver cs4281_pci_driver = { - .name = "cs4281", - .id_table = cs4281_pci_tbl, - .probe = cs4281_probe, - .remove = __devexit_p(cs4281_remove), - .suspend = CS4281_SUSPEND_TBL, - .resume = CS4281_RESUME_TBL, -}; - -static int __init cs4281_init_module(void) -{ - int rtn = 0; - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4281: cs4281_init_module()+ \n")); - printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " " - __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION, - CS4281_ARCH); - rtn = pci_register_driver(&cs4281_pci_driver); - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn)); - return rtn; -} - -static void __exit cs4281_cleanup_module(void) -{ - pci_unregister_driver(&cs4281_pci_driver); - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n")); -} -// --------------------------------------------------------------------- - -MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); -MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); -MODULE_LICENSE("GPL"); - -// --------------------------------------------------------------------- - -module_init(cs4281_init_module); -module_exit(cs4281_cleanup_module); - diff --git a/sound/oss/cs4281/cs4281pm-24.c b/sound/oss/cs4281/cs4281pm-24.c deleted file mode 100644 index 90cbd76795..0000000000 --- a/sound/oss/cs4281/cs4281pm-24.c +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* -* -* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.com). -* -* 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. -* -* 12/22/00 trw - new file. -* -*******************************************************************************/ - -#ifndef NOT_CS4281_PM -#include - -static int cs4281_suspend(struct cs4281_state *s); -static int cs4281_resume(struct cs4281_state *s); -/* -* for now (12/22/00) only enable the pm_register PM support. -* allow these table entries to be null. -#define CS4281_SUSPEND_TBL cs4281_suspend_tbl -#define CS4281_RESUME_TBL cs4281_resume_tbl -*/ -#define CS4281_SUSPEND_TBL cs4281_suspend_null -#define CS4281_RESUME_TBL cs4281_resume_null - -#else /* CS4281_PM */ -#define CS4281_SUSPEND_TBL cs4281_suspend_null -#define CS4281_RESUME_TBL cs4281_resume_null -#endif /* CS4281_PM */ - diff --git a/sound/oss/cs4281/cs4281pm.h b/sound/oss/cs4281/cs4281pm.h deleted file mode 100644 index b44fdc9ce0..0000000000 --- a/sound/oss/cs4281/cs4281pm.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef NOT_CS4281_PM -/******************************************************************************* -* -* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver. -* -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.com). -* -* 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. -* -* 12/22/00 trw - new file. -* -*******************************************************************************/ -/* general pm definitions */ -#define CS4281_AC97_HIGHESTREGTORESTORE 0x26 -#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1) - -/* pipeline definitions */ -#define CS4281_NUMBER_OF_PIPELINES 4 -#define CS4281_PIPELINE_VALID 0x0001 -#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000 -#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001 - -/* PM state defintions */ -#define CS4281_PM_NOT_REGISTERED 0x1000 -#define CS4281_PM_IDLE 0x0001 -#define CS4281_PM_SUSPENDING 0x0002 -#define CS4281_PM_SUSPENDED 0x0004 -#define CS4281_PM_RESUMING 0x0008 -#define CS4281_PM_RESUMED 0x0010 - -struct cs4281_pm { - unsigned long flags; - u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; - u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; - u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; - u32 u32SSPM_BITS; - u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS]; - u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; - u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; - u32 u32hwptr_playback,u32hwptr_capture; -}; - -struct cs4281_pipeline { - unsigned flags; - unsigned number; - u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue; - u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress; - u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress; - u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save; - u32 u32DCCn_Save,u32DCAn_Save; -/* -* technically, these are fifo variables, but just map the -* first fifo with the first pipeline and then use the fifo -* variables inside of the pipeline struct. -*/ - u32 u32FCRn_Save,u32FSICn_Save; - u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress; - u32 u32FPDRnValue,u32FPDRnAddress; -}; -#endif diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c index fb64279f39..08274c995d 100644 --- a/sound/oss/dev_table.c +++ b/sound/oss/dev_table.c @@ -13,9 +13,39 @@ #include -#define _DEV_TABLE_C_ #include "sound_config.h" +struct audio_operations *audio_devs[MAX_AUDIO_DEV]; +EXPORT_SYMBOL(audio_devs); + +int num_audiodevs; +EXPORT_SYMBOL(num_audiodevs); + +struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; +EXPORT_SYMBOL(mixer_devs); + +int num_mixers; +EXPORT_SYMBOL(num_mixers); + +struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; +EXPORT_SYMBOL(synth_devs); + +int num_synths; + +struct midi_operations *midi_devs[MAX_MIDI_DEV]; +EXPORT_SYMBOL(midi_devs); + +int num_midis; +EXPORT_SYMBOL(num_midis); + +struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { + &default_sound_timer, NULL +}; +EXPORT_SYMBOL(sound_timer_devs); + +int num_sound_timers = 1; + + static int sound_alloc_audiodev(void); int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, @@ -75,6 +105,7 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, audio_init_devices(); return num; } +EXPORT_SYMBOL(sound_install_audiodrv); int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, int driver_size, void *devc) @@ -113,6 +144,7 @@ int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, mixer_devs[n] = op; return n; } +EXPORT_SYMBOL(sound_install_mixer); void sound_unload_audiodev(int dev) { @@ -122,6 +154,7 @@ void sound_unload_audiodev(int dev) unregister_sound_dsp((dev<<4)+3); } } +EXPORT_SYMBOL(sound_unload_audiodev); static int sound_alloc_audiodev(void) { @@ -144,6 +177,7 @@ int sound_alloc_mididev(void) num_midis = i + 1; return i; } +EXPORT_SYMBOL(sound_alloc_mididev); int sound_alloc_synthdev(void) { @@ -158,6 +192,7 @@ int sound_alloc_synthdev(void) } return -1; } +EXPORT_SYMBOL(sound_alloc_synthdev); int sound_alloc_mixerdev(void) { @@ -169,6 +204,7 @@ int sound_alloc_mixerdev(void) num_mixers = i + 1; return i; } +EXPORT_SYMBOL(sound_alloc_mixerdev); int sound_alloc_timerdev(void) { @@ -183,6 +219,7 @@ int sound_alloc_timerdev(void) } return -1; } +EXPORT_SYMBOL(sound_alloc_timerdev); void sound_unload_mixerdev(int dev) { @@ -192,6 +229,7 @@ void sound_unload_mixerdev(int dev) num_mixers--; } } +EXPORT_SYMBOL(sound_unload_mixerdev); void sound_unload_mididev(int dev) { @@ -200,15 +238,19 @@ void sound_unload_mididev(int dev) unregister_sound_midi((dev<<4)+2); } } +EXPORT_SYMBOL(sound_unload_mididev); void sound_unload_synthdev(int dev) { if (dev != -1) synth_devs[dev] = NULL; } +EXPORT_SYMBOL(sound_unload_synthdev); void sound_unload_timerdev(int dev) { if (dev != -1) sound_timer_devs[dev] = NULL; } +EXPORT_SYMBOL(sound_unload_timerdev); + diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h index adf1d625b5..b7617bee63 100644 --- a/sound/oss/dev_table.h +++ b/sound/oss/dev_table.h @@ -352,22 +352,8 @@ struct sound_timer_operations void (*arm_timer)(int dev, long time); }; -#ifdef _DEV_TABLE_C_ -struct audio_operations *audio_devs[MAX_AUDIO_DEV]; -int num_audiodevs; -struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; -int num_mixers; -struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; -int num_synths; -struct midi_operations *midi_devs[MAX_MIDI_DEV]; -int num_midis; - extern struct sound_timer_operations default_sound_timer; -struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { - &default_sound_timer, NULL -}; -int num_sound_timers = 1; -#else + extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs; extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; @@ -378,7 +364,6 @@ extern struct midi_operations *midi_devs[MAX_MIDI_DEV]; extern int num_midis; extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; extern int num_sound_timers; -#endif /* _DEV_TABLE_C_ */ extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); void sound_timer_init (struct sound_lowlev_timer *t, char *name); diff --git a/sound/oss/dm.h b/sound/oss/dm.h deleted file mode 100644 index 14a90593c4..0000000000 --- a/sound/oss/dm.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _DRIVERS_SOUND_DM_H -#define _DRIVERS_SOUND_DM_H - -/* - * Definitions of the 'direct midi sound' interface used - * by the newer commercial OSS package. We should export - * this to userland somewhere in glibc later. - */ - -/* - * Data structure composing an FM "note" or sound event. - */ - -struct dm_fm_voice -{ - u8 op; - u8 voice; - u8 am; - u8 vibrato; - u8 do_sustain; - u8 kbd_scale; - u8 harmonic; - u8 scale_level; - u8 volume; - u8 attack; - u8 decay; - u8 sustain; - u8 release; - u8 feedback; - u8 connection; - u8 left; - u8 right; - u8 waveform; -}; - -/* - * This describes an FM note by its voice, octave, frequency number (10bit) - * and key on/off. - */ - -struct dm_fm_note -{ - u8 voice; - u8 octave; - u32 fnum; - u8 key_on; -}; - -/* - * FM parameters that apply globally to all voices, and thus are not "notes" - */ - -struct dm_fm_params -{ - u8 am_depth; - u8 vib_depth; - u8 kbd_split; - u8 rhythm; - - /* This block is the percussion instrument data */ - u8 bass; - u8 snare; - u8 tomtom; - u8 cymbal; - u8 hihat; -}; - -/* - * FM mode ioctl settings - */ - -#define FM_IOCTL_RESET 0x20 -#define FM_IOCTL_PLAY_NOTE 0x21 -#define FM_IOCTL_SET_VOICE 0x22 -#define FM_IOCTL_SET_PARAMS 0x23 -#define FM_IOCTL_SET_MODE 0x24 -#define FM_IOCTL_SET_OPL 0x25 - -#endif diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c index 6c1cf74b78..b256c04011 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -926,6 +926,7 @@ int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) sound_start_dma(dmap, physaddr, count, dma_mode); return count; } +EXPORT_SYMBOL(DMAbuf_start_dma); static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) { @@ -1055,6 +1056,8 @@ void DMAbuf_outputintr(int dev, int notify_only) do_outputintr(dev, notify_only); spin_unlock_irqrestore(&dmap->lock,flags); } +EXPORT_SYMBOL(DMAbuf_outputintr); + /* called with dmap->lock held in irq context */ static void do_inputintr(int dev) { @@ -1154,36 +1157,7 @@ void DMAbuf_inputintr(int dev) do_inputintr(dev); spin_unlock_irqrestore(&dmap->lock,flags); } - -int DMAbuf_open_dma(int dev) -{ - /* - * NOTE! This routine opens only the primary DMA channel (output). - */ - struct audio_operations *adev = audio_devs[dev]; - int err; - - if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0) - return -EBUSY; - dma_init_buffers(adev->dmap_out); - adev->dmap_out->flags |= DMA_ALLOC_DONE; - adev->dmap_out->fragment_size = adev->dmap_out->buffsize; - - if (adev->dmap_out->dma >= 0) { - unsigned long flags; - - flags=claim_dma_lock(); - clear_dma_ff(adev->dmap_out->dma); - disable_dma(adev->dmap_out->dma); - release_dma_lock(flags); - } - return 0; -} - -void DMAbuf_close_dma(int dev) -{ - close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out); -} +EXPORT_SYMBOL(DMAbuf_inputintr); void DMAbuf_init(int dev, int dma1, int dma2) { @@ -1192,12 +1166,6 @@ void DMAbuf_init(int dev, int dma1, int dma2) * NOTE! This routine could be called several times. */ - /* drag in audio_syms.o */ - { - extern char audio_syms_symbol; - audio_syms_symbol = 0; - } - if (adev && adev->dmap_out == NULL) { if (adev->d == NULL) panic("OSS: audio_devs[%d]->d == NULL\n", dev); diff --git a/sound/oss/es1370.c b/sound/oss/es1370.c deleted file mode 100644 index 13f4831497..0000000000 --- a/sound/oss/es1370.c +++ /dev/null @@ -1,2819 +0,0 @@ -/*****************************************************************************/ - -/* - * es1370.c -- Ensoniq ES1370/Asahi Kasei AK4531 audio driver. - * - * Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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. - * - * Special thanks to David C. Niemi - * - * - * Module command line parameters: - * lineout if 1 the LINE jack is used as an output instead of an input. - * LINE then contains the unmixed dsp output. This can be used - * to make the card a four channel one: use dsp to output two - * channels to LINE and dac to output the other two channels to - * SPKR. Set the mixer to only output synth to SPKR. - * micbias sets the +5V bias to the mic if using an electretmic. - * - * - * Note: sync mode is not yet supported (i.e. running dsp and dac from the same - * clock source) - * - * Supported devices: - * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * /dev/dsp1 additional DAC, like /dev/dsp, but output only, - * only 5512, 11025, 22050 and 44100 samples/s, - * outputs to mixer "SYNTH" setting - * /dev/midi simple MIDI UART interface, no ioctl - * - * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed - * to be done in software. That is what /dev/dac is for. By now (Q2 1998) - * there are several MIDI to PCM (WAV) packages, one of them is timidity. - * - * Revision history - * 26.03.1998 0.1 Initial release - * 31.03.1998 0.2 Fix bug in GETOSPACE - * 04.04.1998 0.3 Make it work (again) under 2.0.33 - * Fix mixer write operation not returning the actual - * settings - * 05.04.1998 0.4 First attempt at using the new PCI stuff - * 29.04.1998 0.5 Fix hang when ^C is pressed on amp - * 07.05.1998 0.6 Don't double lock around stop_*() in *_release() - * 10.05.1998 0.7 First stab at a simple midi interface (no bells&whistles) - * 14.05.1998 0.8 Don't allow excessive interrupt rates - * 08.06.1998 0.9 First release using Alan Cox' soundcore instead of - * miscdevice - * 05.07.1998 0.10 Fixed the driver to correctly maintin OSS style volume - * settings (not sure if this should be standard) - * Fixed many references: f_flags should be f_mode - * -- Gerald Britton - * 03.08.1998 0.11 Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se - * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, - * as it produces an annoying ssssh in the lower sampling rate - * Do not include modversions.h - * 22.08.1998 0.12 Mixer registers actually have 5 instead of 4 bits - * pointed out by Itai Nahshon - * 31.08.1998 0.13 Fix realplayer problems - dac.count issues - * 08.10.1998 0.14 Joystick support fixed - * -- Oliver Neukum - * 10.12.1998 0.15 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.1998 0.16 Don't wake up app until there are fragsize bytes to read/write - * 06.01.1999 0.17 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.1999 0.18 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.1999 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.1999 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Note: joystick address handling might still be wrong on archs - * other than i386 - * 10.05.1999 0.21 Added support for an electret mic for SB PCI64 - * to the Linux kernel sound driver. This mod also straighten - * out the question marks around the mic impedance setting - * (micz). From Kim.Berts@fisub.mail.abb.com - * 11.05.1999 0.22 Implemented the IMIX call to mute recording monitor. - * Guenter Geiger - * 15.06.1999 0.23 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.24 Add pci_set_master - * 02.08.1999 0.25 Added workaround for the "phantom write" bug first - * documented by Dave Sharpless from Anchor Games - * 03.08.1999 0.26 adapt to Linus' new __setup/__initcall - * added kernel command line option "es1370=joystick[,lineout[,micbias]]" - * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge - * 12.08.1999 0.27 module_init/__setup fixes - * 19.08.1999 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca - * 31.08.1999 0.29 add spin_lock_init - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.30 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 28.10.1999 0.31 More waitqueue races fixed - * 08.01.2000 0.32 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * 07.02.2000 0.33 Use pci_alloc_consistent and pci_register_driver - * 21.11.2000 0.34 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.35 More dma buffer initializations, patch from - * Tjeerd Mulder - * 07.01.2001 0.36 Timeout change in wrcodec as requested by Frank Klemm - * 31.01.2001 0.37 Register/Unregister gameport - * Fix SETTRIGGER non OSS API conformity - * 03.01.2003 0.38 open_mode fixes from Georg Acher - * - * some important things missing in Ensoniq documentation: - * - * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 - * and vary PCLKDIV to obtain zero beat. - * 5512sps: 254 - * 44100sps: 30 - * seems to be fs = 1411200/(PCLKDIV+2) - * - * should find out when curr_sample_ct is cleared and - * where exactly the CCB fetches data - * - * The card uses a 22.5792 MHz crystal. - * The LINEIN jack may be converted to an AOUT jack by - * setting pin 47 (XCTL0) of the ES1370 to high. - * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic - * - * - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -#define SUPPORT_JOYSTICK -#endif - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS -#define DBG(x) {} -/*#define DBG(x) {x}*/ - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_ENSONIQ -#define PCI_VENDOR_ID_ENSONIQ 0x1274 -#endif - -#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 -#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 -#endif - -#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) - -#define ES1370_EXTENT 0x40 -#define JOY_EXTENT 8 - -#define ES1370_REG_CONTROL 0x00 -#define ES1370_REG_STATUS 0x04 -#define ES1370_REG_UART_DATA 0x08 -#define ES1370_REG_UART_STATUS 0x09 -#define ES1370_REG_UART_CONTROL 0x09 -#define ES1370_REG_UART_TEST 0x0a -#define ES1370_REG_MEMPAGE 0x0c -#define ES1370_REG_CODEC 0x10 -#define ES1370_REG_SERIAL_CONTROL 0x20 -#define ES1370_REG_DAC1_SCOUNT 0x24 -#define ES1370_REG_DAC2_SCOUNT 0x28 -#define ES1370_REG_ADC_SCOUNT 0x2c - -#define ES1370_REG_DAC1_FRAMEADR 0xc30 -#define ES1370_REG_DAC1_FRAMECNT 0xc34 -#define ES1370_REG_DAC2_FRAMEADR 0xc38 -#define ES1370_REG_DAC2_FRAMECNT 0xc3c -#define ES1370_REG_ADC_FRAMEADR 0xd30 -#define ES1370_REG_ADC_FRAMECNT 0xd34 -#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 -#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c - -#define ES1370_FMT_U8_MONO 0 -#define ES1370_FMT_U8_STEREO 1 -#define ES1370_FMT_S16_MONO 2 -#define ES1370_FMT_S16_STEREO 3 -#define ES1370_FMT_STEREO 1 -#define ES1370_FMT_S16 2 -#define ES1370_FMT_MASK 3 - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; - -#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) -#define DAC2_DIVTOSR(x) (1411200/((x)+2)) - -#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ -#define CTRL_XCTL1 0x40000000 /* electret mic bias */ -#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ -#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ -#define CTRL_SH_PCLKDIV 16 -#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ -#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ -#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ -#define CTRL_SH_WTSRSEL 12 -#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ -#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ -#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ -#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ -#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ -#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ -#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ -#define CTRL_ADC_EN 0x00000010 /* enable ADC */ -#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ -#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ -#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ -#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ - -#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ -#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ -#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ -#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ -#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ -#define STAT_SH_VC 5 -#define STAT_MCCB 0x00000010 /* CCB int pending */ -#define STAT_UART 0x00000008 /* UART int pending */ -#define STAT_DAC1 0x00000004 /* DAC1 int pending */ -#define STAT_DAC2 0x00000002 /* DAC2 int pending */ -#define STAT_ADC 0x00000001 /* ADC int pending */ - -#define USTAT_RXINT 0x80 /* UART rx int pending */ -#define USTAT_TXINT 0x04 /* UART tx int pending */ -#define USTAT_TXRDY 0x02 /* UART tx ready */ -#define USTAT_RXRDY 0x01 /* UART rx ready */ - -#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ -#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ -#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ -#define UCTRL_CNTRL 0x03 /* control field */ -#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ - -#define SCTRL_P2ENDINC 0x00380000 /* */ -#define SCTRL_SH_P2ENDINC 19 -#define SCTRL_P2STINC 0x00070000 /* */ -#define SCTRL_SH_P2STINC 16 -#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ -#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ -#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ -#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ -#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ -#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ -#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ -#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ -#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ -#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ -#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ -#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ -#define SCTRL_R1FMT 0x00000030 /* format mask */ -#define SCTRL_SH_R1FMT 4 -#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ -#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ -#define SCTRL_P2FMT 0x0000000c /* format mask */ -#define SCTRL_SH_P2FMT 2 -#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ -#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ -#define SCTRL_P1FMT 0x00000003 /* format mask */ -#define SCTRL_SH_P1FMT 0 - -/* misc stuff */ - -#define FMODE_DAC 4 /* slight misuse of mode_t */ - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -/* --------------------------------------------------------------------- */ - -struct es1370_state { - /* magic */ - unsigned int magic; - - /* list of es1370 devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_mixer; - int dev_dac; - int dev_midi; - - /* hardware resources */ - unsigned long io; /* long for SPARC */ - unsigned int irq; - - /* mixer registers; there is no HW readback */ - struct { - unsigned short vol[10]; - unsigned int recsrc; - unsigned int modcnt; - unsigned short micpreamp; - unsigned int imix; - } mix; - - /* wave stuff */ - unsigned ctrl; - unsigned sctrl; - - spinlock_t lock; - struct mutex open_mutex; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac1, dma_dac2, dma_adc; - - /* The following buffer is used to point the phantom write channel to. */ - unsigned char *bugbuf_cpu; - dma_addr_t bugbuf_dma; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - -#ifdef SUPPORT_JOYSTICK - struct gameport *gameport; -#endif - - struct mutex mutex; -}; - -/* --------------------------------------------------------------------- */ - -static LIST_HEAD(devs); - -/* --------------------------------------------------------------------- */ - -static inline unsigned ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -/* --------------------------------------------------------------------- */ - -static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) -{ - unsigned long tmo = jiffies + HZ/10, j; - - do { - j = jiffies; - if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { - outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); - return; - } - schedule(); - } while ((signed)(tmo-j) > 0); - printk(KERN_ERR "es1370: write to codec register timeout\n"); -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_adc(struct es1370_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_ADC_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac1(struct es1370_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_DAC1_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac2(struct es1370_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ctrl &= ~CTRL_DAC2_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac1(struct es1370_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) - && s->dma_dac1.ready) { - s->ctrl |= CTRL_DAC1_EN; - s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_dac1.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac2(struct es1370_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) - && s->dma_dac2.ready) { - s->ctrl |= CTRL_DAC2_EN; - s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | - SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | - (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | - (0 << SCTRL_SH_P2STINC); - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_dac2.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct es1370_state *s) -{ - unsigned long flags; - unsigned fragremain, fshift; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->ctrl |= CTRL_ADC_EN; - s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); - fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; - if (fragremain < 2*fshift) - fragremain = s->dma_adc.fragsize; - outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static inline void dealloc_dmabuf(struct es1370_state *s, struct dmabuf *db) -{ - struct page *page, *pend; - - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) -{ - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - fmt &= ES1370_FMT_MASK; - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); - outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); - outl(db->dmaaddr, s->io+(reg & 0xff)); - outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); - db->enabled = 1; - db->ready = 1; - return 0; -} - -static inline int prog_dmabuf_adc(struct es1370_state *s) -{ - stop_adc(s); - return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), - (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); -} - -static inline int prog_dmabuf_dac2(struct es1370_state *s) -{ - stop_dac2(s); - return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), - (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); -} - -static inline int prog_dmabuf_dac1(struct es1370_state *s) -{ - stop_dac1(s); - return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], - (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); -} - -static inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) -{ - unsigned hwptr, diff; - - outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); - hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; - diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; - db->hwptr = hwptr; - return diff; -} - -static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) -{ - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(((char *)buf) + bptr, c, x); - bptr = 0; - len -= x; - } - memset(((char *)buf) + bptr, c, len); -} - -/* call with spinlock held! */ -static void es1370_update_ptr(struct es1370_state *s) -{ - int diff; - - /* update ADC pointer */ - if (s->ctrl & CTRL_ADC_EN) { - diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->ctrl &= ~CTRL_ADC_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - s->dma_adc.error++; - } - } - } - /* update DAC1 pointer */ - if (s->ctrl & CTRL_DAC1_EN) { - diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); - s->dma_dac1.total_bytes += diff; - if (s->dma_dac1.mapped) { - s->dma_dac1.count += diff; - if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) - wake_up(&s->dma_dac1.wait); - } else { - s->dma_dac1.count -= diff; - if (s->dma_dac1.count <= 0) { - s->ctrl &= ~CTRL_DAC1_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - s->dma_dac1.error++; - } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { - clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, - s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); - s->dma_dac1.endcleared = 1; - } - if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) - wake_up(&s->dma_dac1.wait); - } - } - /* update DAC2 pointer */ - if (s->ctrl & CTRL_DAC2_EN) { - diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); - s->dma_dac2.total_bytes += diff; - if (s->dma_dac2.mapped) { - s->dma_dac2.count += diff; - if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) - wake_up(&s->dma_dac2.wait); - } else { - s->dma_dac2.count -= diff; - if (s->dma_dac2.count <= 0) { - s->ctrl &= ~CTRL_DAC2_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - s->dma_dac2.error++; - } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { - clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, - s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); - s->dma_dac2.endcleared = 1; - } - if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) - wake_up(&s->dma_dac2.wait); - } - } -} - -/* hold spinlock for the following! */ -static void es1370_handle_midi(struct es1370_state *s) -{ - unsigned char ch; - int wake; - - if (!(s->ctrl & CTRL_UART_EN)) - return; - wake = 0; - while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { - ch = inb(s->io+ES1370_REG_UART_DATA); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); - outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); -} - -static irqreturn_t es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct es1370_state *s = (struct es1370_state *)dev_id; - unsigned int intsrc, sctl; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inl(s->io+ES1370_REG_STATUS); - if (!(intsrc & 0x80000000)) - return IRQ_NONE; - spin_lock(&s->lock); - /* clear audio interrupts first */ - sctl = s->sctrl; - if (intsrc & STAT_ADC) - sctl &= ~SCTRL_R1INTEN; - if (intsrc & STAT_DAC1) - sctl &= ~SCTRL_P1INTEN; - if (intsrc & STAT_DAC2) - sctl &= ~SCTRL_P2INTEN; - outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - es1370_update_ptr(s); - es1370_handle_midi(s); - spin_unlock(&s->lock); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != ES1370_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -static const struct { - unsigned volidx:4; - unsigned left:4; - unsigned right:4; - unsigned stereo:1; - unsigned recmask:13; - unsigned avail:1; -} mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ - [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ - [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ - [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ - [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ - [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ - [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 }, /* Mono1 */ - [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 }, /* Mono2 */ - [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 }, /* Mic */ - [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ -}; - -static void set_recsrc(struct es1370_state *s, unsigned int val) -{ - unsigned int i, j; - - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].recmask) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].recmask; - } - s->mix.recsrc = val; - wrcodec(s, 0x12, j & 0xd5); - wrcodec(s, 0x13, j & 0xaa); - wrcodec(s, 0x14, (j >> 8) & 0x17); - wrcodec(s, 0x15, (j >> 8) & 0x0f); - i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; - if (!s->mix.imix) { - i &= 0xff60; /* mute record and line monitor */ - } - wrcodec(s, 0x10, i); - wrcodec(s, 0x11, i >> 8); -} - -static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val; - unsigned char l, r, rl, rr; - int __user *p = (int __user *)arg; - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_PRIVATE1) { - /* enable/disable/query mixer preamp */ - if (get_user(val, p)) - return -EFAULT; - if (val != -1) { - s->mix.micpreamp = !!val; - wrcodec(s, 0x19, s->mix.micpreamp); - } - return put_user(s->mix.micpreamp, p); - } - if (cmd == SOUND_MIXER_PRIVATE2) { - /* enable/disable/query use of linein as second lineout */ - if (get_user(val, p)) - return -EFAULT; - if (val != -1) { - spin_lock_irqsave(&s->lock, flags); - if (val) - s->ctrl |= CTRL_XCTL0; - else - s->ctrl &= ~CTRL_XCTL0; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->ctrl & CTRL_XCTL0) ? 1 : 0, p); - } - if (cmd == SOUND_MIXER_PRIVATE3) { - /* enable/disable/query microphone impedance setting */ - if (get_user(val, p)) - return -EFAULT; - if (val != -1) { - spin_lock_irqsave(&s->lock, flags); - if (val) - s->ctrl |= CTRL_XCTL1; - else - s->ctrl &= ~CTRL_XCTL1; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->ctrl & CTRL_XCTL1) ? 1 : 0, p); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strncpy(info.id, "ES1370", sizeof(info.id)); - strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); - info.modify_counter = s->mix.modcnt; - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strncpy(info.id, "ES1370", sizeof(info.id)); - strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(s->mix.recsrc, p); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - val = SOUND_MASK_IMIX; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].avail) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].recmask) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].stereo) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_CAPS: - return put_user(0, p); - - case SOUND_MIXER_IMIX: - return put_user(s->mix.imix, p); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) - return -EINVAL; - return put_user(s->mix.vol[mixtable[i].volidx], p); - } - } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - switch (_IOC_NR(cmd)) { - - case SOUND_MIXER_IMIX: - if (get_user(s->mix.imix, p)) - return -EFAULT; - set_recsrc(s, s->mix.recsrc); - return 0; - - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, p)) - return -EFAULT; - set_recsrc(s, val); - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (mixtable[i].stereo) { - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - if (l < 7) { - rl = 0x80; - l = 0; - } else { - rl = 31 - ((l - 7) / 3); - l = (31 - rl) * 3 + 7; - } - if (r < 7) { - rr = 0x80; - r = 0; - } else { - rr = 31 - ((r - 7) / 3); - r = (31 - rr) * 3 + 7; - } - wrcodec(s, mixtable[i].right, rr); - } else { - if (mixtable[i].left == 15) { - if (l < 2) { - rr = rl = 0x80; - r = l = 0; - } else { - rl = 7 - ((l - 2) / 14); - r = l = (7 - rl) * 14 + 2; - } - } else { - if (l < 7) { - rl = 0x80; - r = l = 0; - } else { - rl = 31 - ((l - 7) / 3); - r = l = (31 - rl) * 3 + 7; - } - } - } - wrcodec(s, mixtable[i].left, rl); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l; -#else - s->mix.vol[mixtable[i].volidx] = val; -#endif - return put_user(s->mix.vol[mixtable[i].volidx], p); - } -} - -/* --------------------------------------------------------------------- */ - -static int es1370_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (s->dev_mixer == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - return nonseekable_open(inode, file); -} - -static int es1370_release_mixdev(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations es1370_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = es1370_ioctl_mixdev, - .open = es1370_open_mixdev, - .release = es1370_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac1(struct es1370_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac1.mapped || !s->dma_dac1.ready) - return 0; - add_wait_queue(&s->dma_dac1.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac1.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 - / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; - tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static int drain_dac2(struct es1370_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac2.mapped || !s->dma_dac2.ready) - return 0; - add_wait_queue(&s->dma_dac2.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac2.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 - / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); - tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo + 1)) - DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) - } - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t es1370_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - mutex_lock(&s->mutex); - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - goto out; - - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto out; - } - mutex_unlock(&s->mutex); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto out; - } - mutex_lock(&s->mutex); - if (s->dma_adc.mapped) - { - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); - } -out: - mutex_unlock(&s->mutex); - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t es1370_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac2.mapped) - return -ENXIO; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - mutex_lock(&s->mutex); - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - goto out; - ret = 0; - add_wait_queue(&s->dma_dac2.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac2.count < 0) { - s->dma_dac2.count = 0; - s->dma_dac2.swptr = s->dma_dac2.hwptr; - } - swptr = s->dma_dac2.swptr; - cnt = s->dma_dac2.dmasize-swptr; - if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) - cnt = s->dma_dac2.dmasize - s->dma_dac2.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac2.enabled) - start_dac2(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - goto out; - } - mutex_unlock(&s->mutex); - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - goto out; - } - mutex_lock(&s->mutex); - if (s->dma_dac2.mapped) - { - ret = -ENXIO; - goto out; - } - continue; - } - if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - goto out; - } - swptr = (swptr + cnt) % s->dma_dac2.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac2.swptr = swptr; - s->dma_dac2.count += cnt; - s->dma_dac2.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac2.enabled) - start_dac2(s); - } -out: - mutex_unlock(&s->mutex); - remove_wait_queue(&s->dma_dac2.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) - return 0; - poll_wait(file, &s->dma_dac2.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac2.mapped) { - if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1370_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - struct dmabuf *db; - int ret = 0; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - mutex_lock(&s->mutex); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac2(s)) != 0) { - goto out; - } - db = &s->dma_dac2; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) { - goto out; - } - db = &s->dma_adc; - } else { - ret = -EINVAL; - goto out; - } - if (vma->vm_pgoff != 0) { - ret = -EINVAL; - goto out; - } - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) { - ret = -EINVAL; - goto out; - } - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) { - ret = -EAGAIN; - goto out; - } - db->mapped = 1; -out: - mutex_unlock(&s->mutex); - unlock_kernel(); - return ret; -} - -static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - synchronize_irq(s->irq); - s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) - return -EINVAL; - if (val < 4000) - val = 4000; - if (val > 50000) - val = 50000; - stop_adc(s); - stop_dac2(s); - s->dma_adc.ready = s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_R1SMB; - else - s->sctrl &= ~SCTRL_R1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_P2SMB; - else - s->sctrl &= ~SCTRL_P2SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_R1SMB; - else - s->sctrl &= ~SCTRL_R1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_P2SMB; - else - s->sctrl &= ~SCTRL_P2SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - } - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_R1SEB; - else - s->sctrl &= ~SCTRL_R1SEB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - s->dma_dac2.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_P2SEB; - else - s->sctrl &= ~SCTRL_P2SEB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - } - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? - AFMT_S16_LE : AFMT_U8, p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - return ret; - s->dma_adc.enabled = 1; - start_adc(s); - } else { - s->dma_adc.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - return ret; - s->dma_dac2.enabled = 1; - start_dac2(s); - } else { - s->dma_dac2.enabled = 0; - stop_dac2(s); - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - abinfo.fragsize = s->dma_dac2.fragsize; - count = s->dma_dac2.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac2.dmasize - count; - abinfo.fragstotal = s->dma_dac2.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - count = s->dma_adc.count; - if (count < 0) - count = 0; - abinfo.bytes = count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - count = s->dma_dac2.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - cinfo.bytes = s->dma_dac2.total_bytes; - count = s->dma_dac2.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac2.fragshift; - cinfo.ptr = s->dma_dac2.hwptr; - if (s->dma_dac2.mapped) - s->dma_dac2.count &= s->dma_dac2.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf_dac2(s))) - return val; - return put_user(s->dma_dac2.fragsize, p); - } - if ((val = prog_dmabuf_adc(s))) - return val; - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac2.ossfragshift = val & 0xffff; - s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac2.ossfragshift < 4) - s->dma_dac2.ossfragshift = 4; - if (s->dma_dac2.ossfragshift > 15) - s->dma_dac2.ossfragshift = 15; - if (s->dma_dac2.ossmaxfrags < 4) - s->dma_dac2.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac2.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), p); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? - 2 : 1, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? - 16 : 8, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return mixer_ioctl(s, cmd, arg); -} - -static int es1370_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) - s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - s->sctrl &= ~SCTRL_R1FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; - else - s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; - s->dma_dac2.enabled = 1; - s->sctrl &= ~SCTRL_P2FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; - else - s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; - } - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - mutex_unlock(&s->open_mutex); - mutex_init(&s->mutex); - return nonseekable_open(inode, file); -} - -static int es1370_release(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac2(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_mutex); - if (file->f_mode & FMODE_WRITE) { - stop_dac2(s); - synchronize_irq(s->irq); - dealloc_dmabuf(s, &s->dma_dac2); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1370_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = es1370_read, - .write = es1370_write, - .poll = es1370_poll, - .ioctl = es1370_ioctl, - .mmap = es1370_mmap, - .open = es1370_open, - .release = es1370_release, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t es1370_write_dac(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac1.mapped) - return -ENXIO; - if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - add_wait_queue(&s->dma_dac1.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac1.count < 0) { - s->dma_dac1.count = 0; - s->dma_dac1.swptr = s->dma_dac1.hwptr; - } - swptr = s->dma_dac1.swptr; - cnt = s->dma_dac1.dmasize-swptr; - if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) - cnt = s->dma_dac1.dmasize - s->dma_dac1.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac1.enabled) - start_dac1(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac1.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac1.swptr = swptr; - s->dma_dac1.count += cnt; - s->dma_dac1.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac1.enabled) - start_dac1(s); - } - remove_wait_queue(&s->dma_dac1.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) - return 0; - poll_wait(file, &s->dma_dac1.wait, wait); - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - if (s->dma_dac1.mapped) { - if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - int ret; - unsigned long size; - - VALIDATE_STATE(s); - if (!(vma->vm_flags & VM_WRITE)) - return -EINVAL; - lock_kernel(); - if ((ret = prog_dmabuf_dac1(s)) != 0) - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << s->dma_dac1.buforder)) - goto out; - ret = -EAGAIN; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(s->dma_dac1.rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - s->dma_dac1.mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - unsigned ctrl; - int val, ret; - int __user *p = (int __user *)arg; - - VALIDATE_STATE(s); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); - - case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: - stop_dac1(s); - synchronize_irq(s->irq); - s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - stop_dac1(s); - s->dma_dac1.ready = 0; - for (ctrl = 0; ctrl <= 2; ctrl++) - if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) - break; - spin_lock_irqsave(&s->lock, flags); - s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val) - s->sctrl |= SCTRL_P1SMB; - else - s->sctrl &= ~SCTRL_P1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) { - if (s->dma_dac1.mapped) - return -EINVAL; - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val >= 2) - s->sctrl |= SCTRL_P1SMB; - else - s->sctrl &= ~SCTRL_P1SMB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - stop_dac1(s); - s->dma_dac1.ready = 0; - spin_lock_irqsave(&s->lock, flags); - if (val == AFMT_S16_LE) - s->sctrl |= SCTRL_P1SEB; - else - s->sctrl &= ~SCTRL_P1SEB; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - } - return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) - return ret; - s->dma_dac1.enabled = 1; - start_dac1(s); - } else { - s->dma_dac1.enabled = 0; - stop_dac1(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - abinfo.fragsize = s->dma_dac1.fragsize; - count = s->dma_dac1.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac1.dmasize - count; - abinfo.fragstotal = s->dma_dac1.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - count = s->dma_dac1.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, p); - - case SNDCTL_DSP_GETOPTR: - if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - es1370_update_ptr(s); - cinfo.bytes = s->dma_dac1.total_bytes; - count = s->dma_dac1.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac1.fragshift; - cinfo.ptr = s->dma_dac1.hwptr; - if (s->dma_dac1.mapped) - s->dma_dac1.count &= s->dma_dac1.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if ((val = prog_dmabuf_dac1(s))) - return val; - return put_user(s->dma_dac1.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - s->dma_dac1.ossfragshift = val & 0xffff; - s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac1.ossfragshift < 4) - s->dma_dac1.ossfragshift = 4; - if (s->dma_dac1.ossfragshift > 15) - s->dma_dac1.ossfragshift = 15; - if (s->dma_dac1.ossmaxfrags < 4) - s->dma_dac1.ossmaxfrags = 4; - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if (s->dma_dac1.subdivision) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - s->dma_dac1.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], p); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return mixer_ioctl(s, cmd, arg); -} - -static int es1370_open_dac(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (!((s->dev_dac ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - /* we allow opening with O_RDWR, most programs do it although they will only write */ -#if 0 - if (file->f_mode & FMODE_READ) - return -EPERM; -#endif - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & FMODE_DAC) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; - s->dma_dac1.enabled = 1; - spin_lock_irqsave(&s->lock, flags); - s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); - s->sctrl &= ~SCTRL_P1FMT; - if ((minor & 0xf) == SND_DEV_DSP16) - s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; - else - s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= FMODE_DAC; - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int es1370_release_dac(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - drain_dac1(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_mutex); - stop_dac1(s); - dealloc_dmabuf(s, &s->dma_dac1); - s->open_mode &= ~FMODE_DAC; - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1370_dac_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = es1370_write_dac, - .poll = es1370_poll_dac, - .ioctl = es1370_ioctl_dac, - .mmap = es1370_mmap_dac, - .open = es1370_open_dac, - .release = es1370_release_dac, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t es1370_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t es1370_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - es1370_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - es1370_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int es1370_midi_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct es1370_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct es1370_state, devs); - if (s->dev_midi == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); - outb(0, s->io+ES1370_REG_UART_CONTROL); - outb(0, s->io+ES1370_REG_UART_TEST); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - s->ctrl |= CTRL_UART_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - es1370_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int es1370_midi_release(struct inode *inode, struct file *file) -{ - struct es1370_state *s = (struct es1370_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (file->f_flags & O_NONBLOCK) - break; - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - mutex_lock(&s->open_mutex); - s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE)); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->ctrl &= ~CTRL_UART_EN; - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - } - spin_unlock_irqrestore(&s->lock, flags); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations es1370_midi_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = es1370_midi_read, - .write = es1370_midi_write, - .poll = es1370_midi_poll, - .open = es1370_midi_open, - .release = es1370_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static int lineout[NR_DEVICE]; -static int micbias[NR_DEVICE]; - -static unsigned int devindex; - -module_param_array(lineout, bool, NULL, 0); -MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); -module_param_array(micbias, bool, NULL, 0); -MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); -MODULE_LICENSE("GPL"); - - -/* --------------------------------------------------------------------- */ - -static struct initvol { - int mixch; - int vol; -} initvol[] __devinitdata = { - { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, - { SOUND_MIXER_WRITE_PCM, 0x4040 }, - { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, - { SOUND_MIXER_WRITE_CD, 0x4040 }, - { SOUND_MIXER_WRITE_LINE, 0x4040 }, - { SOUND_MIXER_WRITE_LINE1, 0x4040 }, - { SOUND_MIXER_WRITE_LINE2, 0x4040 }, - { SOUND_MIXER_WRITE_LINE3, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 }, - { SOUND_MIXER_WRITE_OGAIN, 0x4040 } -}; - -#ifdef SUPPORT_JOYSTICK - -static int __devinit es1370_register_gameport(struct es1370_state *s) -{ - struct gameport *gp; - - if (!request_region(0x200, JOY_EXTENT, "es1370")) { - printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); - return -EBUSY; - } - - s->gameport = gp = gameport_allocate_port(); - if (!gp) { - printk(KERN_ERR "es1370: can not allocate memory for gameport\n"); - release_region(0x200, JOY_EXTENT); - return -ENOMEM; - } - - gameport_set_name(gp, "ESS1370"); - gameport_set_phys(gp, "pci%s/gameport0", pci_name(s->dev)); - gp->dev.parent = &s->dev->dev; - gp->io = 0x200; - - s->ctrl |= CTRL_JYSTK_EN; - outl(s->ctrl, s->io + ES1370_REG_CONTROL); - - gameport_register_port(gp); - - return 0; -} - -static inline void es1370_unregister_gameport(struct es1370_state *s) -{ - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, JOY_EXTENT); - - } -} - -#else -static inline int es1370_register_gameport(struct es1370_state *s) { return -ENOSYS; } -static inline void es1370_unregister_gameport(struct es1370_state *s) { } -#endif /* SUPPORT_JOYSTICK */ - -static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - struct es1370_state *s; - mm_segment_t fs; - int i, val, ret; - - if ((ret=pci_enable_device(pcidev))) - return ret; - - if ( !(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || - !pci_resource_start(pcidev, 0) - ) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - i = pci_set_dma_mask(pcidev, DMA_32BIT_MASK); - if (i) { - printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n"); - return i; - } - if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { - printk(KERN_WARNING "es1370: out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct es1370_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac1.wait); - init_waitqueue_head(&s->dma_dac2.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - mutex_init(&s->open_mutex); - spin_lock_init(&s->lock); - s->magic = ES1370_MAGIC; - s->dev = pcidev; - s->io = pci_resource_start(pcidev, 0); - s->irq = pcidev->irq; - if (!request_region(s->io, ES1370_EXTENT, "es1370")) { - printk(KERN_ERR "es1370: io ports %#lx-%#lx in use\n", s->io, s->io+ES1370_EXTENT-1); - ret = -EBUSY; - goto err_region; - } - if ((ret=request_irq(s->irq, es1370_interrupt, IRQF_SHARED, "es1370",s))) { - printk(KERN_ERR "es1370: irq %u in use\n", s->irq); - goto err_irq; - } - - /* initialize codec registers */ - /* note: setting CTRL_SERR_DIS is reported to break - * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ - s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); - if (lineout[devindex]) - s->ctrl |= CTRL_XCTL0; - if (micbias[devindex]) - s->ctrl |= CTRL_XCTL1; - s->sctrl = 0; - printk(KERN_INFO "es1370: adapter at io %#lx irq %u, line %s, mic impedance %s\n", - s->io, s->irq, (s->ctrl & CTRL_XCTL0) ? "out" : "in", - (s->ctrl & CTRL_XCTL1) ? "1" : "0"); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) { - ret = s->dev_dac; - goto err_dev3; - } - if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) { - ret = s->dev_midi; - goto err_dev4; - } - /* initialize the chips */ - outl(s->ctrl, s->io+ES1370_REG_CONTROL); - outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); - /* point phantom write channel to "bugbuf" */ - s->bugbuf_cpu = pci_alloc_consistent(pcidev,16,&s->bugbuf_dma); - if (!s->bugbuf_cpu) { - ret = -ENOMEM; - goto err_dev5; - } - outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); - outl(s->bugbuf_dma, s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); - outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); - pci_set_master(pcidev); /* enable bus mastering */ - wrcodec(s, 0x16, 3); /* no RST, PD */ - wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ - wrcodec(s, 0x18, 0); /* recording source is mixer */ - wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ - s->mix.imix = 1; - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); - } - set_fs(fs); - - es1370_register_gameport(s); - - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; - - err_dev5: - unregister_sound_midi(s->dev_midi); - err_dev4: - unregister_sound_dsp(s->dev_dac); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "es1370: cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->io, ES1370_EXTENT); - err_region: - kfree(s); - return ret; -} - -static void __devexit es1370_remove(struct pci_dev *dev) -{ - struct es1370_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); - outl(CTRL_SERR_DIS | (1 << CTRL_SH_WTSRSEL), s->io+ES1370_REG_CONTROL); /* switch everything off */ - outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ - synchronize_irq(s->irq); - free_irq(s->irq, s); - es1370_unregister_gameport(s); - release_region(s->io, ES1370_EXTENT); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_dsp(s->dev_dac); - unregister_sound_midi(s->dev_midi); - pci_free_consistent(dev, 16, s->bugbuf_cpu, s->bugbuf_dma); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] = { - { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver es1370_driver = { - .name = "es1370", - .id_table = id_table, - .probe = es1370_probe, - .remove = __devexit_p(es1370_remove), -}; - -static int __init init_es1370(void) -{ - printk(KERN_INFO "es1370: version v0.38 time " __TIME__ " " __DATE__ "\n"); - return pci_register_driver(&es1370_driver); -} - -static void __exit cleanup_es1370(void) -{ - printk(KERN_INFO "es1370: unloading\n"); - pci_unregister_driver(&es1370_driver); -} - -module_init(init_es1370); -module_exit(cleanup_es1370); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* format is: es1370=lineout[,micbias]] */ - -static int __init es1370_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= NR_DEVICE) - return 0; - - (void) - ((get_option(&str,&lineout [nr_dev]) == 2) - && get_option(&str,&micbias [nr_dev]) - ); - - nr_dev++; - return 1; -} - -__setup("es1370=", es1370_setup); - -#endif /* MODULE */ diff --git a/sound/oss/esssolo1.c b/sound/oss/esssolo1.c deleted file mode 100644 index 82f40a0a5c..0000000000 --- a/sound/oss/esssolo1.c +++ /dev/null @@ -1,2516 +0,0 @@ -/****************************************************************************/ - -/* - * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. - * - * Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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. - * - * Module command line parameters: - * none so far - * - * Supported devices: - * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * /dev/midi simple MIDI UART interface, no ioctl - * - * Revision history - * 10.11.1998 0.1 Initial release (without any hardware) - * 22.03.1999 0.2 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 07.04.1999 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * 15.06.1999 0.4 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.5 Add pci_set_master - * 12.08.1999 0.6 Fix MIDI UART crashing the driver - * Changed mixer semantics from OSS documented - * behaviour to OSS "code behaviour". - * Recording might actually work now. - * The real DDMA controller address register is at PCI config - * 0x60, while the register at 0x18 is used as a placeholder - * register for BIOS address allocation. This register - * is supposed to be copied into 0x60, according - * to the Solo1 datasheet. When I do that, I can access - * the DDMA registers except the mask bit, which - * is stuck at 1. When I copy the contents of 0x18 +0x10 - * to the DDMA base register, everything seems to work. - * The fun part is that the Windows Solo1 driver doesn't - * seem to do these tricks. - * Bugs remaining: plops and clicks when starting/stopping playback - * 31.08.1999 0.7 add spin_lock_init - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.8 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 07.10.1999 0.9 Fix initialization; complain if sequencer writes time out - * Revised resource grabbing for the FM synthesizer - * 28.10.1999 0.10 More waitqueue races fixed - * 09.12.1999 0.11 Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) - * Disabling recording on Alpha - * 12.01.2000 0.12 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * Integrated (aka redid 8-)) APM support patch by Zach Brown - * 07.02.2000 0.13 Use pci_alloc_consistent and pci_register_driver - * 19.02.2000 0.14 Use pci_dma_supported to determine if recording should be disabled - * 13.03.2000 0.15 Reintroduce initialization of a couple of PCI config space registers - * 21.11.2000 0.16 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.17 More dma buffer initializations, patch from - * Tjeerd Mulder - * 31.01.2001 0.18 Register/Unregister gameport, original patch from - * Nathaniel Daw - * Fix SETTRIGGER non OSS API conformity - * 10.03.2001 provide abs function, prevent picking up a bogus kernel macro - * for abs. Bug report by Andrew Morton - * 15.05.2001 pci_enable_device moved, return values in probe cleaned - * up. Marcus Meissner - * 22.05.2001 0.19 more cleanups, changed PM to PCI 2.4 style, got rid - * of global list of devices, using pci device data. - * Marcus Meissner - * 03.01.2003 0.20 open_mode fixes from Georg Acher - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - -#include "dm.h" - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_ESS -#define PCI_VENDOR_ID_ESS 0x125d -#endif -#ifndef PCI_DEVICE_ID_ESS_SOLO1 -#define PCI_DEVICE_ID_ESS_SOLO1 0x1969 -#endif - -#define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) - -#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ -#define DDMABASE_EXTENT 16 - -#define IOBASE_EXTENT 16 -#define SBBASE_EXTENT 16 -#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) -#define MPUBASE_EXTENT 4 -#define GPBASE_EXTENT 4 -#define GAMEPORT_EXTENT 4 - -#define FMSYNTH_EXTENT 4 - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 3 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define FMODE_DMFM 0x10 - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -#define SUPPORT_JOYSTICK 1 -#endif - -static struct pci_driver solo1_driver; - -/* --------------------------------------------------------------------- */ - -struct solo1_state { - /* magic */ - unsigned int magic; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_mixer; - int dev_midi; - int dev_dmfm; - - /* hardware resources */ - unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */ - unsigned int irq; - - /* mixer registers */ - struct { - unsigned short vol[10]; - unsigned int recsrc; - unsigned int modcnt; - unsigned short micpreamp; - } mix; - - /* wave stuff */ - unsigned fmt; - unsigned channels; - unsigned rate; - unsigned char clkdiv; - unsigned ena; - - spinlock_t lock; - struct mutex open_mutex; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - -#if SUPPORT_JOYSTICK - struct gameport *gameport; -#endif -}; - -/* --------------------------------------------------------------------- */ - -static inline void write_seq(struct solo1_state *s, unsigned char data) -{ - int i; - unsigned long flags; - - /* the local_irq_save stunt is to send the data within the command window */ - for (i = 0; i < 0xffff; i++) { - local_irq_save(flags); - if (!(inb(s->sbbase+0xc) & 0x80)) { - outb(data, s->sbbase+0xc); - local_irq_restore(flags); - return; - } - local_irq_restore(flags); - } - printk(KERN_ERR "esssolo1: write_seq timeout\n"); - outb(data, s->sbbase+0xc); -} - -static inline int read_seq(struct solo1_state *s, unsigned char *data) -{ - int i; - - if (!data) - return 0; - for (i = 0; i < 0xffff; i++) - if (inb(s->sbbase+0xe) & 0x80) { - *data = inb(s->sbbase+0xa); - return 1; - } - printk(KERN_ERR "esssolo1: read_seq timeout\n"); - return 0; -} - -static inline int reset_ctrl(struct solo1_state *s) -{ - int i; - - outb(3, s->sbbase+6); /* clear sequencer and FIFO */ - udelay(10); - outb(0, s->sbbase+6); - for (i = 0; i < 0xffff; i++) - if (inb(s->sbbase+0xe) & 0x80) - if (inb(s->sbbase+0xa) == 0xaa) { - write_seq(s, 0xc6); /* enter enhanced mode */ - return 1; - } - return 0; -} - -static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data) -{ - write_seq(s, reg); - write_seq(s, data); -} - -#if 0 /* unused */ -static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) -{ - unsigned char r; - - write_seq(s, 0xc0); - write_seq(s, reg); - read_seq(s, &r); - return r; -} -#endif /* unused */ - -static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) -{ - outb(reg, s->sbbase+4); - outb(data, s->sbbase+5); -} - -static unsigned char read_mixer(struct solo1_state *s, unsigned char reg) -{ - outb(reg, s->sbbase+4); - return inb(s->sbbase+5); -} - -/* --------------------------------------------------------------------- */ - -static inline unsigned ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_dac(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_WRITE; - write_mixer(s, 0x78, 0x10); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { - s->ena |= FMODE_WRITE; - write_mixer(s, 0x78, 0x12); - udelay(10); - write_mixer(s, 0x78, 0x13); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_adc(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_READ; - write_ctrl(s, 0xb8, 0xe); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct solo1_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->ena |= FMODE_READ; - write_ctrl(s, 0xb8, 0xf); -#if 0 - printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); -#endif - outb(0, s->ddmabase+0xd); /* master reset */ - outb(1, s->ddmabase+0xf); /* mask */ - outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ - outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); - outw(s->dma_adc.dmasize-1, s->ddmabase+4); - outb(0, s->ddmabase+0xf); - } - spin_unlock_irqrestore(&s->lock, flags); -#if 0 - printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" - KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->sbbase+0xc), - inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); - printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" - KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", - read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), - read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), - read_ctrl(s, 0xb9)); -#endif -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static inline void dealloc_dmabuf(struct solo1_state *s, struct dmabuf *db) -{ - struct page *page, *pend; - - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - -static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db) -{ - int order; - unsigned bytespersec; - unsigned bufs, sample_shift = 0; - struct page *page, *pend; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - sample_shift++; - if (s->channels > 1) - sample_shift++; - bytespersec = s->rate << sample_shift; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytespersec) - db->fragshift = ld2(bytespersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift; - db->dmasize = db->numfrag << db->fragshift; - db->enabled = 1; - return 0; -} - -static inline int prog_dmabuf_adc(struct solo1_state *s) -{ - unsigned long va; - int c; - - stop_adc(s); - /* check if PCI implementation supports 24bit busmaster DMA */ - if (s->dev->dma_mask > 0xffffff) - return -EIO; - if ((c = prog_dmabuf(s, &s->dma_adc))) - return c; - va = s->dma_adc.dmaaddr; - if ((va & ~((1<<24)-1))) - panic("solo1: buffer above 16M boundary"); - outb(0, s->ddmabase+0xd); /* clear */ - outb(1, s->ddmabase+0xf); /* mask */ - /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ - outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ - outl(va, s->ddmabase); - outw(s->dma_adc.dmasize-1, s->ddmabase+4); - c = - s->dma_adc.fragsamples; - write_ctrl(s, 0xa4, c); - write_ctrl(s, 0xa5, c >> 8); - outb(0, s->ddmabase+0xf); - s->dma_adc.ready = 1; - return 0; -} - -static int prog_dmabuf_dac(struct solo1_state *s) -{ - unsigned long va; - int c; - - stop_dac(s); - if ((c = prog_dmabuf(s, &s->dma_dac))) - return c; - memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ - va = s->dma_dac.dmaaddr; - if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) - panic("solo1: buffer crosses 1M boundary"); - outl(va, s->iobase); - /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ - outw(s->dma_dac.dmasize, s->iobase+4); - c = - s->dma_dac.fragsamples; - write_mixer(s, 0x74, c); - write_mixer(s, 0x76, c >> 8); - outb(0xa, s->iobase+6); - s->dma_dac.ready = 1; - return 0; -} - -static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) -{ - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(((char *)buf) + bptr, c, x); - bptr = 0; - len -= x; - } - memset(((char *)buf) + bptr, c, len); -} - -/* call with spinlock held! */ - -static void solo1_update_ptr(struct solo1_state *s) -{ - int diff; - unsigned hwptr; - - /* update ADC pointer */ - if (s->ena & FMODE_READ) { - hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; -#if 0 - printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", - s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); -#endif - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - } else { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->ena &= ~FMODE_READ; - write_ctrl(s, 0xb8, 0xe); - s->dma_adc.error++; - } - if (s->dma_adc.count > 0) - wake_up(&s->dma_adc.wait); - } - } - /* update DAC pointer */ - if (s->ena & FMODE_WRITE) { - hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; -#if 0 - printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", - s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count); -#endif - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - s->ena &= ~FMODE_WRITE; - write_mixer(s, 0x78, 0x12); - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, - s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count < (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); - } - } -} - -/* --------------------------------------------------------------------- */ - -static void prog_codec(struct solo1_state *s) -{ - unsigned long flags; - int fdiv, filter; - unsigned char c; - - reset_ctrl(s); - write_seq(s, 0xd3); - /* program sampling rates */ - filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ - fdiv = 256 - 7160000 / (filter * 82); - spin_lock_irqsave(&s->lock, flags); - write_ctrl(s, 0xa1, s->clkdiv); - write_ctrl(s, 0xa2, fdiv); - write_mixer(s, 0x70, s->clkdiv); - write_mixer(s, 0x72, fdiv); - /* program ADC parameters */ - write_ctrl(s, 0xb8, 0xe); - write_ctrl(s, 0xb9, /*0x1*/0); - write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); - c = 0xd0; - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - c |= 0x04; - if (s->fmt & (AFMT_S16_LE | AFMT_S8)) - c |= 0x20; - if (s->channels > 1) - c ^= 0x48; - write_ctrl(s, 0xb7, (c & 0x70) | 1); - write_ctrl(s, 0xb7, c); - write_ctrl(s, 0xb1, 0x50); - write_ctrl(s, 0xb2, 0x50); - /* program DAC parameters */ - c = 0x40; - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - c |= 1; - if (s->fmt & (AFMT_S16_LE | AFMT_S8)) - c |= 4; - if (s->channels > 1) - c |= 2; - write_mixer(s, 0x7a, c); - write_mixer(s, 0x78, 0x10); - s->ena = 0; - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != SOLO1_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg) -{ - static const unsigned int mixer_src[8] = { - SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, - SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 - }; - static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 1, /* voice */ - [SOUND_MIXER_SYNTH] = 2, /* FM */ - [SOUND_MIXER_CD] = 3, /* CD */ - [SOUND_MIXER_LINE] = 4, /* Line */ - [SOUND_MIXER_LINE1] = 5, /* AUX */ - [SOUND_MIXER_MIC] = 6, /* Mic */ - [SOUND_MIXER_LINE2] = 7, /* Mono in */ - [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ - [SOUND_MIXER_RECLEV] = 9, /* Recording level */ - [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ - }; - static const unsigned char mixreg[] = { - 0x7c, /* voice */ - 0x36, /* FM */ - 0x38, /* CD */ - 0x3e, /* Line */ - 0x3a, /* AUX */ - 0x1a, /* Mic */ - 0x6d /* Mono in */ - }; - unsigned char l, r, rl, rr, vidx; - int i, val; - int __user *p = (int __user *)arg; - - VALIDATE_STATE(s); - - if (cmd == SOUND_MIXER_PRIVATE1) { - /* enable/disable/query mixer preamp */ - if (get_user(val, p)) - return -EFAULT; - if (val != -1) { - val = val ? 0xff : 0xf7; - write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); - } - val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; - return put_user(val, p); - } - if (cmd == SOUND_MIXER_PRIVATE2) { - /* enable/disable/query spatializer */ - if (get_user(val, p)) - return -EFAULT; - if (val != -1) { - val &= 0x3f; - write_mixer(s, 0x52, val); - write_mixer(s, 0x50, val ? 0x08 : 0); - } - return put_user(read_mixer(s, 0x52), p); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strncpy(info.id, "Solo1", sizeof(info.id)); - strncpy(info.name, "ESS Solo1", sizeof(info.name)); - info.modify_counter = s->mix.modcnt; - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strncpy(info.id, "Solo1", sizeof(info.id)); - strncpy(info.name, "ESS Solo1", sizeof(info.name)); - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(mixer_src[read_mixer(s, 0x1c) & 7], p); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | - SOUND_MASK_SPEAKER, p); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, p); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | - SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | - SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV, p); - - case SOUND_MIXER_CAPS: - return put_user(SOUND_CAP_EXCL_INPUT, p); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) - return -EINVAL; - return put_user(s->mix.vol[vidx-1], p); - } - } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ -#if 0 - { - static const unsigned char regs[] = { - 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c - }; - int i; - - for (i = 0; i < sizeof(regs); i++) - printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", - regs[i], read_mixer(s, regs[i])); - printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", - 0xb4, read_ctrl(s, 0xb4)); - } -#endif - if (get_user(val, p)) - return -EFAULT; - i = hweight32(val); - if (i == 0) - return 0; - else if (i > 1) - val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; - for (i = 0; i < 8; i++) { - if (mixer_src[i] & val) - break; - } - if (i > 7) - return 0; - write_mixer(s, 0x1c, i); - return 0; - - case SOUND_MIXER_VOLUME: - if (get_user(val, p)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - if (l < 6) { - rl = 0x40; - l = 0; - } else { - rl = (l * 2 - 11) / 3; - l = (rl * 3 + 11) / 2; - } - if (r < 6) { - rr = 0x40; - r = 0; - } else { - rr = (r * 2 - 11) / 3; - r = (rr * 3 + 11) / 2; - } - write_mixer(s, 0x60, rl); - write_mixer(s, 0x62, rr); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[9] = ((unsigned int)r << 8) | l; -#else - s->mix.vol[9] = val; -#endif - return put_user(s->mix.vol[9], p); - - case SOUND_MIXER_SPEAKER: - if (get_user(val, p)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - else if (l < 2) - l = 2; - rl = (l - 2) / 14; - l = rl * 14 + 2; - write_mixer(s, 0x3c, rl); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[7] = l * 0x101; -#else - s->mix.vol[7] = val; -#endif - return put_user(s->mix.vol[7], p); - - case SOUND_MIXER_RECLEV: - if (get_user(val, p)) - return -EFAULT; - l = (val << 1) & 0x1fe; - if (l > 200) - l = 200; - else if (l < 5) - l = 5; - r = (val >> 7) & 0x1fe; - if (r > 200) - r = 200; - else if (r < 5) - r = 5; - rl = (l - 5) / 13; - rr = (r - 5) / 13; - r = (rl * 13 + 5) / 2; - l = (rr * 13 + 5) / 2; - write_ctrl(s, 0xb4, (rl << 4) | rr); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[8] = ((unsigned int)r << 8) | l; -#else - s->mix.vol[8] = val; -#endif - return put_user(s->mix.vol[8], p); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - l = (val << 1) & 0x1fe; - if (l > 200) - l = 200; - else if (l < 5) - l = 5; - r = (val >> 7) & 0x1fe; - if (r > 200) - r = 200; - else if (r < 5) - r = 5; - rl = (l - 5) / 13; - rr = (r - 5) / 13; - r = (rl * 13 + 5) / 2; - l = (rr * 13 + 5) / 2; - write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[vidx-1] = ((unsigned int)r << 8) | l; -#else - s->mix.vol[vidx-1] = val; -#endif - return put_user(s->mix.vol[vidx-1], p); - } -} - -/* --------------------------------------------------------------------- */ - -static int solo1_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct solo1_state *s = NULL; - struct pci_dev *pci_dev = NULL; - - while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { - struct pci_driver *drvr; - drvr = pci_dev_driver (pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (s->dev_mixer == minor) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - return nonseekable_open(inode, file); -} - -static int solo1_release_mixdev(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations solo1_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = solo1_ioctl_mixdev, - .open = solo1_open_mixdev, - .release = solo1_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct solo1_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - unsigned tmo; - - if (s->dma_dac.mapped) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; - if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) - tmo >>= 1; - if (s->channels > 1) - tmo >>= 1; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "solo1: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t solo1_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); -#endif - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" - KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" - KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" - KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), - read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), - inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); -#endif - if (inb(s->ddmabase+15) & 1) - printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" - KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" - KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" - KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), - read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), - inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); -#endif - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); -#endif - } - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t solo1_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; -#if 0 - printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x 71: 0x%02x 72: 0x%02x 74: 0x%02x 76: 0x%02x 78: 0x%02x 7A: 0x%02x\n" - KERN_DEBUG "solo1_write: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x SBstat: 0x%02x\n", - read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76), - read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); - printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x reg 7A: 0x%02x DMAcnt: 0x%04x DMAstat: 0x%02x SBstat: 0x%02x\n", - read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); -#endif - ret = 0; - add_wait_queue(&s->dma_dac.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize-swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac.enabled) - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac.enabled) - start_dac(s); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } else { - if (s->dma_adc.count > 0) - mask |= POLLIN | POLLRDNORM; - } - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize > s->dma_dac.count) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - - -static int solo1_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_dac(s)) != 0) - goto out; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_adc(s)) != 0) - goto out; - db = &s->dma_adc; - } else - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EAGAIN; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret, count; - int div1, div2; - unsigned rate1, rate2; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - prog_codec(s); - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program sampling rates */ - if (val > 48000) - val = 48000; - if (val < 6300) - val = 6300; - div1 = (768000 + val / 2) / val; - rate1 = (768000 + div1 / 2) / div1; - div1 = -div1; - div2 = (793800 + val / 2) / val; - rate2 = (793800 + div2 / 2) / div2; - div2 = (-div2) & 0x7f; - if (abs(val - rate2) < abs(val - rate1)) { - rate1 = rate2; - div1 = div2; - } - s->rate = rate1; - s->clkdiv = div1; - prog_codec(s); - } - return put_user(s->rate, p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program channels */ - s->channels = val ? 2 : 1; - prog_codec(s); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) { - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program channels */ - s->channels = (val >= 2) ? 2 : 1; - prog_codec(s); - } - return put_user(s->channels, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - stop_adc(s); - stop_dac(s); - s->dma_adc.ready = s->dma_dac.ready = 0; - /* program format */ - if (val != AFMT_S16_LE && val != AFMT_U16_LE && - val != AFMT_S8 && val != AFMT_U8) - val = AFMT_U8; - s->fmt = val; - prog_codec(s); - } - return put_user(s->fmt, p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & s->ena & FMODE_READ) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & s->ena & FMODE_WRITE) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - return ret; - s->dma_dac.enabled = 1; - start_adc(s); - if (inb(s->ddmabase+15) & 1) - printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); - } else { - s->dma_dac.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) - return ret; - s->dma_dac.enabled = 1; - start_dac(s); - } else { - s->dma_dac.enabled = 0; - stop_dac(s); - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - count = s->dma_dac.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac.dmasize - count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - solo1_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); -#if 0 - printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" - KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", - cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, - s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples); -#endif - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf_dac(s))) - return val; - return put_user(s->dma_dac.fragsize, p); - } - if ((val = prog_dmabuf_adc(s))) - return val; - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user(s->rate, p); - - case SOUND_PCM_READ_CHANNELS: - return put_user(s->channels, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & (AFMT_S8|AFMT_U8)) ? 8 : 16, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return mixer_ioctl(s, cmd, arg); -} - -static int solo1_release(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_mutex); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - outb(0, s->iobase+6); /* disable DMA */ - dealloc_dmabuf(s, &s->dma_dac); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - outb(1, s->ddmabase+0xf); /* mask DMA channel */ - outb(0, s->ddmabase+0xd); /* DMA master clear */ - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= ~(FMODE_READ | FMODE_WRITE); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static int solo1_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - struct solo1_state *s = NULL; - struct pci_dev *pci_dev = NULL; - - while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { - struct pci_driver *drvr; - - drvr = pci_dev_driver(pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - s->fmt = AFMT_U8; - s->channels = 1; - s->rate = 8000; - s->clkdiv = 96 | 0x80; - s->ena = 0; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - s->dma_dac.enabled = 1; - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - mutex_unlock(&s->open_mutex); - prog_codec(s); - return nonseekable_open(inode, file); -} - -static /*const*/ struct file_operations solo1_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = solo1_read, - .write = solo1_write, - .poll = solo1_poll, - .ioctl = solo1_ioctl, - .mmap = solo1_mmap, - .open = solo1_open, - .release = solo1_release, -}; - -/* --------------------------------------------------------------------- */ - -/* hold spinlock for the following! */ -static void solo1_handle_midi(struct solo1_state *s) -{ - unsigned char ch; - int wake; - - if (!(s->mpubase)) - return; - wake = 0; - while (!(inb(s->mpubase+1) & 0x80)) { - ch = inb(s->mpubase); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->mpubase); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} - -static irqreturn_t solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct solo1_state *s = (struct solo1_state *)dev_id; - unsigned int intsrc; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inb(s->iobase+7); /* get interrupt source(s) */ - if (!intsrc) - return IRQ_NONE; - (void)inb(s->sbbase+0xe); /* clear interrupt */ - spin_lock(&s->lock); - /* clear audio interrupts first */ - if (intsrc & 0x20) - write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); - solo1_update_ptr(s); - solo1_handle_midi(s); - spin_unlock(&s->lock); - return IRQ_HANDLED; -} - -static void solo1_midi_timer(unsigned long data) -{ - struct solo1_state *s = (struct solo1_state *)data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - solo1_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies+1; - add_timer(&s->midi.timer); -} - -/* --------------------------------------------------------------------- */ - -static ssize_t solo1_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t solo1_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - solo1_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - solo1_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_flags & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_flags & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_flags & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_flags & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int solo1_midi_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct solo1_state *s = NULL; - struct pci_dev *pci_dev = NULL; - - while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { - struct pci_driver *drvr; - - drvr = pci_dev_driver(pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (s->dev_midi == minor) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - outb(0xff, s->mpubase+1); /* reset command */ - outb(0x3f, s->mpubase+1); /* uart command */ - if (!(inb(s->mpubase+1) & 0x80)) - inb(s->mpubase); - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies+1; - s->midi.timer.data = (unsigned long)s; - s->midi.timer.function = solo1_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int solo1_midi_release(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (file->f_flags & O_NONBLOCK) - break; - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "solo1: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - mutex_lock(&s->open_mutex); - s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE)); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ - del_timer(&s->midi.timer); - } - spin_unlock_irqrestore(&s->lock, flags); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations solo1_midi_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = solo1_midi_read, - .write = solo1_midi_write, - .poll = solo1_midi_poll, - .open = solo1_midi_open, - .release = solo1_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - static const unsigned char op_offset[18] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 - }; - struct solo1_state *s = (struct solo1_state *)file->private_data; - struct dm_fm_voice v; - struct dm_fm_note n; - struct dm_fm_params p; - unsigned int io; - unsigned int regb; - - switch (cmd) { - case FM_IOCTL_RESET: - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->sbbase); - outb(0, s->sbbase+1); - outb(regb, s->sbbase+2); - outb(0, s->sbbase+3); - } - return 0; - - case FM_IOCTL_PLAY_NOTE: - if (copy_from_user(&n, (void __user *)arg, sizeof(n))) - return -EFAULT; - if (n.voice >= 18) - return -EINVAL; - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->sbbase+2; - } else { - regb = n.voice; - io = s->sbbase; - } - outb(0xa0 + regb, io); - outb(n.fnum & 0xff, io+1); - outb(0xb0 + regb, io); - outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); - return 0; - - case FM_IOCTL_SET_VOICE: - if (copy_from_user(&v, (void __user *)arg, sizeof(v))) - return -EFAULT; - if (v.voice >= 18) - return -EINVAL; - regb = op_offset[v.voice]; - io = s->sbbase + ((v.op & 1) << 1); - outb(0x20 + regb, io); - outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | - ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); - outb(0x40 + regb, io); - outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); - outb(0x60 + regb, io); - outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); - outb(0x80 + regb, io); - outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); - outb(0xe0 + regb, io); - outb(v.waveform & 0x7, io+1); - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->sbbase+2; - } else { - regb = n.voice; - io = s->sbbase; - } - outb(0xc0 + regb, io); - outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | - (v.connection & 1), io+1); - return 0; - - case FM_IOCTL_SET_PARAMS: - if (copy_from_user(&p, (void __user *)arg, sizeof(p))) - return -EFAULT; - outb(0x08, s->sbbase); - outb((p.kbd_split & 1) << 6, s->sbbase+1); - outb(0xbd, s->sbbase); - outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | - ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); - return 0; - - case FM_IOCTL_SET_OPL: - outb(4, s->sbbase+2); - outb(arg, s->sbbase+3); - return 0; - - case FM_IOCTL_SET_MODE: - outb(5, s->sbbase+2); - outb(arg & 1, s->sbbase+3); - return 0; - - default: - return -EINVAL; - } -} - -static int solo1_dmfm_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - struct solo1_state *s = NULL; - struct pci_dev *pci_dev = NULL; - - while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { - struct pci_driver *drvr; - - drvr = pci_dev_driver(pci_dev); - if (drvr != &solo1_driver) - continue; - s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - continue; - if (s->dev_dmfm == minor) - break; - } - if (!s) - return -ENODEV; - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & FMODE_DMFM) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) { - mutex_unlock(&s->open_mutex); - printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); - return -EBUSY; - } - /* init the stuff */ - outb(1, s->sbbase); - outb(0x20, s->sbbase+1); /* enable waveforms */ - outb(4, s->sbbase+2); - outb(0, s->sbbase+3); /* no 4op enabled */ - outb(5, s->sbbase+2); - outb(1, s->sbbase+3); /* enable OPL3 */ - s->open_mode |= FMODE_DMFM; - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int solo1_dmfm_release(struct inode *inode, struct file *file) -{ - struct solo1_state *s = (struct solo1_state *)file->private_data; - unsigned int regb; - - VALIDATE_STATE(s); - lock_kernel(); - mutex_lock(&s->open_mutex); - s->open_mode &= ~FMODE_DMFM; - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->sbbase); - outb(0, s->sbbase+1); - outb(regb, s->sbbase+2); - outb(0, s->sbbase+3); - } - release_region(s->sbbase, FMSYNTH_EXTENT); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations solo1_dmfm_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = solo1_dmfm_ioctl, - .open = solo1_dmfm_open, - .release = solo1_dmfm_release, -}; - -/* --------------------------------------------------------------------- */ - -static struct initvol { - int mixch; - int vol; -} initvol[] __devinitdata = { - { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, - { SOUND_MIXER_WRITE_PCM, 0x4040 }, - { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, - { SOUND_MIXER_WRITE_CD, 0x4040 }, - { SOUND_MIXER_WRITE_LINE, 0x4040 }, - { SOUND_MIXER_WRITE_LINE1, 0x4040 }, - { SOUND_MIXER_WRITE_LINE2, 0x4040 }, - { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, - { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 } -}; - -static int setup_solo1(struct solo1_state *s) -{ - struct pci_dev *pcidev = s->dev; - mm_segment_t fs; - int i, val; - - /* initialize DDMA base address */ - printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); - pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); - /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ - pci_write_config_dword(pcidev, 0x50, 0); - /* disable legacy audio address decode */ - pci_write_config_word(pcidev, 0x40, 0x907f); - - /* initialize the chips */ - if (!reset_ctrl(s)) { - printk(KERN_ERR "esssolo1: cannot reset controller\n"); - return -1; - } - outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ - - /* initialize mixer regs */ - write_mixer(s, 0x7f, 0); /* disable music digital recording */ - write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */ - write_mixer(s, 0x64, 0x45); /* volume control */ - write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */ - write_mixer(s, 0x50, 0); /* disable spatializer */ - write_mixer(s, 0x52, 0); - write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ - write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ - outb(0, s->ddmabase+0xd); /* DMA master clear */ - outb(1, s->ddmabase+0xf); /* mask channel */ - /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ - - pci_set_master(pcidev); /* enable bus mastering */ - - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); - } - val = 1; /* enable mic preamp */ - mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); - set_fs(fs); - return 0; -} - -static int -solo1_suspend(struct pci_dev *pci_dev, pm_message_t state) { - struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - return 1; - outb(0, s->iobase+6); - /* DMA master clear */ - outb(0, s->ddmabase+0xd); - /* reset sequencer and FIFO */ - outb(3, s->sbbase+6); - /* turn off DDMA controller address space */ - pci_write_config_word(s->dev, 0x60, 0); - return 0; -} - -static int -solo1_resume(struct pci_dev *pci_dev) { - struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); - if (!s) - return 1; - setup_solo1(s); - return 0; -} - -#ifdef SUPPORT_JOYSTICK -static int __devinit solo1_register_gameport(struct solo1_state *s, int io_port) -{ - struct gameport *gp; - - if (!request_region(io_port, GAMEPORT_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: gameport io ports are in use\n"); - return -EBUSY; - } - - s->gameport = gp = gameport_allocate_port(); - if (!gp) { - printk(KERN_ERR "solo1: can not allocate memory for gameport\n"); - release_region(io_port, GAMEPORT_EXTENT); - return -ENOMEM; - } - - gameport_set_name(gp, "ESS Solo1 Gameport"); - gameport_set_phys(gp, "isa%04x/gameport0", io_port); - gp->dev.parent = &s->dev->dev; - gp->io = io_port; - - gameport_register_port(gp); - - return 0; -} - -static inline void solo1_unregister_gameport(struct solo1_state *s) -{ - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, GAMEPORT_EXTENT); - } -} -#else -static inline int solo1_register_gameport(struct solo1_state *s, int io_port) { return -ENOSYS; } -static inline void solo1_unregister_gameport(struct solo1_state *s) { } -#endif /* SUPPORT_JOYSTICK */ - -static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - struct solo1_state *s; - int gpio; - int ret; - - if ((ret=pci_enable_device(pcidev))) - return ret; - if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || - !(pci_resource_flags(pcidev, 1) & IORESOURCE_IO) || - !(pci_resource_flags(pcidev, 2) & IORESOURCE_IO) || - !(pci_resource_flags(pcidev, 3) & IORESOURCE_IO)) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - - /* Recording requires 24-bit DMA, so attempt to set dma mask - * to 24 bits first, then 32 bits (playback only) if that fails. - */ - if (pci_set_dma_mask(pcidev, DMA_24BIT_MASK) && - pci_set_dma_mask(pcidev, DMA_32BIT_MASK)) { - printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n"); - return -ENODEV; - } - - if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) { - printk(KERN_WARNING "solo1: out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct solo1_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - mutex_init(&s->open_mutex); - spin_lock_init(&s->lock); - s->magic = SOLO1_MAGIC; - s->dev = pcidev; - s->iobase = pci_resource_start(pcidev, 0); - s->sbbase = pci_resource_start(pcidev, 1); - s->vcbase = pci_resource_start(pcidev, 2); - s->ddmabase = s->vcbase + DDMABASE_OFFSET; - s->mpubase = pci_resource_start(pcidev, 3); - gpio = pci_resource_start(pcidev, 4); - s->irq = pcidev->irq; - ret = -EBUSY; - if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region1; - } - if (!request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region2; - } - if (!request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region3; - } - if (!request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1")) { - printk(KERN_ERR "solo1: io ports in use\n"); - goto err_region4; - } - if ((ret=request_irq(s->irq,solo1_interrupt,IRQF_SHARED,"ESS Solo1",s))) { - printk(KERN_ERR "solo1: irq %u in use\n", s->irq); - goto err_irq; - } - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) { - ret = s->dev_midi; - goto err_dev3; - } - if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) { - ret = s->dev_dmfm; - goto err_dev4; - } - if (setup_solo1(s)) { - ret = -EIO; - goto err; - } - /* register gameport */ - solo1_register_gameport(s, gpio); - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - return 0; - - err: - unregister_sound_special(s->dev_dmfm); - err_dev4: - unregister_sound_midi(s->dev_midi); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "solo1: initialisation error\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->mpubase, MPUBASE_EXTENT); - err_region4: - release_region(s->ddmabase, DDMABASE_EXTENT); - err_region3: - release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); - err_region2: - release_region(s->iobase, IOBASE_EXTENT); - err_region1: - kfree(s); - return ret; -} - -static void __devexit solo1_remove(struct pci_dev *dev) -{ - struct solo1_state *s = pci_get_drvdata(dev); - - if (!s) - return; - /* stop DMA controller */ - outb(0, s->iobase+6); - outb(0, s->ddmabase+0xd); /* DMA master clear */ - outb(3, s->sbbase+6); /* reset sequencer and FIFO */ - synchronize_irq(s->irq); - pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */ - free_irq(s->irq, s); - solo1_unregister_gameport(s); - release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); - release_region(s->ddmabase, DDMABASE_EXTENT); - release_region(s->mpubase, MPUBASE_EXTENT); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_midi(s->dev_midi); - unregister_sound_special(s->dev_dmfm); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] = { - { PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver solo1_driver = { - .name = "ESS Solo1", - .id_table = id_table, - .probe = solo1_probe, - .remove = __devexit_p(solo1_remove), - .suspend = solo1_suspend, - .resume = solo1_resume, -}; - - -static int __init init_solo1(void) -{ - printk(KERN_INFO "solo1: version v0.20 time " __TIME__ " " __DATE__ "\n"); - return pci_register_driver(&solo1_driver); -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ESS Solo1 Driver"); -MODULE_LICENSE("GPL"); - - -static void __exit cleanup_solo1(void) -{ - printk(KERN_INFO "solo1: unloading\n"); - pci_unregister_driver(&solo1_driver); -} - -/* --------------------------------------------------------------------- */ - -module_init(init_solo1); -module_exit(cleanup_solo1); - diff --git a/sound/oss/forte.c b/sound/oss/forte.c deleted file mode 100644 index ea1c0207ae..0000000000 --- a/sound/oss/forte.c +++ /dev/null @@ -1,2139 +0,0 @@ -/* - * forte.c - ForteMedia FM801 OSS Driver - * - * Written by Martin K. Petersen - * Copyright (C) 2002 Hewlett-Packard Company - * Portions Copyright (C) 2003 Martin K. Petersen - * - * Latest version: http://mkp.net/forte/ - * - * Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers - * by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks - * guys! - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#define DRIVER_NAME "forte" -#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $" -#define PFX DRIVER_NAME ": " - -#undef M_DEBUG - -#ifdef M_DEBUG -#define DPRINTK(args...) printk(KERN_WARNING args) -#else -#define DPRINTK(args...) -#endif - -/* Card capabilities */ -#define FORTE_CAPS (DSP_CAP_MMAP | DSP_CAP_TRIGGER) - -/* Supported audio formats */ -#define FORTE_FMTS (AFMT_U8 | AFMT_S16_LE) - -/* Buffers */ -#define FORTE_MIN_FRAG_SIZE 256 -#define FORTE_MAX_FRAG_SIZE PAGE_SIZE -#define FORTE_DEF_FRAG_SIZE 256 -#define FORTE_MIN_FRAGMENTS 2 -#define FORTE_MAX_FRAGMENTS 256 -#define FORTE_DEF_FRAGMENTS 2 -#define FORTE_MIN_BUF_MSECS 500 -#define FORTE_MAX_BUF_MSECS 1000 - -/* PCI BARs */ -#define FORTE_PCM_VOL 0x00 /* PCM Output Volume */ -#define FORTE_FM_VOL 0x02 /* FM Output Volume */ -#define FORTE_I2S_VOL 0x04 /* I2S Volume */ -#define FORTE_REC_SRC 0x06 /* Record Source */ -#define FORTE_PLY_CTRL 0x08 /* Playback Control */ -#define FORTE_PLY_COUNT 0x0a /* Playback Count */ -#define FORTE_PLY_BUF1 0x0c /* Playback Buffer I */ -#define FORTE_PLY_BUF2 0x10 /* Playback Buffer II */ -#define FORTE_CAP_CTRL 0x14 /* Capture Control */ -#define FORTE_CAP_COUNT 0x16 /* Capture Count */ -#define FORTE_CAP_BUF1 0x18 /* Capture Buffer I */ -#define FORTE_CAP_BUF2 0x1c /* Capture Buffer II */ -#define FORTE_CODEC_CTRL 0x22 /* Codec Control */ -#define FORTE_I2S_MODE 0x24 /* I2S Mode Control */ -#define FORTE_VOLUME 0x26 /* Volume Up/Down/Mute Status */ -#define FORTE_I2C_CTRL 0x29 /* I2C Control */ -#define FORTE_AC97_CMD 0x2a /* AC'97 Command */ -#define FORTE_AC97_DATA 0x2c /* AC'97 Data */ -#define FORTE_MPU401_DATA 0x30 /* MPU401 Data */ -#define FORTE_MPU401_CMD 0x31 /* MPU401 Command */ -#define FORTE_GPIO_CTRL 0x52 /* General Purpose I/O Control */ -#define FORTE_GEN_CTRL 0x54 /* General Control */ -#define FORTE_IRQ_MASK 0x56 /* Interrupt Mask */ -#define FORTE_IRQ_STATUS 0x5a /* Interrupt Status */ -#define FORTE_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ -#define FORTE_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ -#define FORTE_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ -#define FORTE_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ -#define FORTE_POWERDOWN 0x70 /* Blocks Power Down Control */ - -#define FORTE_CAP_OFFSET FORTE_CAP_CTRL - FORTE_PLY_CTRL - -#define FORTE_AC97_ADDR_SHIFT 10 - -/* Playback and record control register bits */ -#define FORTE_BUF1_LAST (1<<1) -#define FORTE_BUF2_LAST (1<<2) -#define FORTE_START (1<<5) -#define FORTE_PAUSE (1<<6) -#define FORTE_IMMED_STOP (1<<7) -#define FORTE_RATE_SHIFT 8 -#define FORTE_RATE_MASK (15 << FORTE_RATE_SHIFT) -#define FORTE_CHANNELS_4 (1<<12) /* Playback only */ -#define FORTE_CHANNELS_6 (2<<12) /* Playback only */ -#define FORTE_CHANNELS_6MS (3<<12) /* Playback only */ -#define FORTE_CHANNELS_MASK (3<<12) -#define FORTE_16BIT (1<<14) -#define FORTE_STEREO (1<<15) - -/* IRQ status bits */ -#define FORTE_IRQ_PLAYBACK (1<<8) -#define FORTE_IRQ_CAPTURE (1<<9) -#define FORTE_IRQ_VOLUME (1<<14) -#define FORTE_IRQ_MPU (1<<15) - -/* CODEC control */ -#define FORTE_CC_CODEC_RESET (1<<5) -#define FORTE_CC_AC97_RESET (1<<6) - -/* AC97 cmd */ -#define FORTE_AC97_WRITE (0<<7) -#define FORTE_AC97_READ (1<<7) -#define FORTE_AC97_DP_INVALID (0<<8) -#define FORTE_AC97_DP_VALID (1<<8) -#define FORTE_AC97_PORT_RDY (0<<9) -#define FORTE_AC97_PORT_BSY (1<<9) - - -struct forte_channel { - const char *name; - - unsigned short ctrl; /* Ctrl BAR contents */ - unsigned long iobase; /* Ctrl BAR address */ - - wait_queue_head_t wait; - - void *buf; /* Buffer */ - dma_addr_t buf_handle; /* Buffer handle */ - - unsigned int record; - unsigned int format; - unsigned int rate; - unsigned int stereo; - - unsigned int frag_sz; /* Current fragment size */ - unsigned int frag_num; /* Current # of fragments */ - unsigned int frag_msecs; /* Milliseconds per frag */ - unsigned int buf_sz; /* Current buffer size */ - - unsigned int hwptr; /* Tail */ - unsigned int swptr; /* Head */ - unsigned int filled_frags; /* Fragments currently full */ - unsigned int next_buf; /* Index of next buffer */ - - unsigned int active; /* Channel currently in use */ - unsigned int mapped; /* mmap */ - - unsigned int buf_pages; /* Real size of buffer */ - unsigned int nr_irqs; /* Number of interrupts */ - unsigned int bytes; /* Total bytes */ - unsigned int residue; /* Partial fragment */ -}; - - -struct forte_chip { - struct pci_dev *pci_dev; - unsigned long iobase; - int irq; - - struct mutex open_mutex; /* Device access */ - spinlock_t lock; /* State */ - - spinlock_t ac97_lock; - struct ac97_codec *ac97; - - int multichannel; - int dsp; /* OSS handle */ - int trigger; /* mmap I/O trigger */ - - struct forte_channel play; - struct forte_channel rec; -}; - - -static int channels[] = { 2, 4, 6, }; -static int rates[] = { 5500, 8000, 9600, 11025, 16000, 19200, - 22050, 32000, 38400, 44100, 48000, }; - -static struct forte_chip *forte; -static int found; - - -/* AC97 Codec -------------------------------------------------------------- */ - - -/** - * forte_ac97_wait: - * @chip: fm801 instance whose AC97 codec to wait on - * - * FIXME: - * Stop busy-waiting - */ - -static inline int -forte_ac97_wait (struct forte_chip *chip) -{ - int i = 10000; - - while ( (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_PORT_BSY) - && i-- ) - cpu_relax(); - - return i == 0; -} - - -/** - * forte_ac97_read: - * @codec: AC97 codec to read from - * @reg: register to read - */ - -static u16 -forte_ac97_read (struct ac97_codec *codec, u8 reg) -{ - u16 ret = 0; - struct forte_chip *chip = codec->private_data; - - spin_lock (&chip->ac97_lock); - - /* Knock, knock */ - if (forte_ac97_wait (chip)) { - printk (KERN_ERR PFX "ac97_read: Serial bus busy\n"); - goto out; - } - - /* Send read command */ - outw (reg | (1<<7), chip->iobase + FORTE_AC97_CMD); - - if (forte_ac97_wait (chip)) { - printk (KERN_ERR PFX "ac97_read: Bus busy reading reg 0x%x\n", - reg); - goto out; - } - - /* Sanity checking */ - if (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_DP_INVALID) { - printk (KERN_ERR PFX "ac97_read: Invalid data port"); - goto out; - } - - /* Fetch result */ - ret = inw (chip->iobase + FORTE_AC97_DATA); - - out: - spin_unlock (&chip->ac97_lock); - return ret; -} - - -/** - * forte_ac97_write: - * @codec: AC97 codec to send command to - * @reg: register to write - * @val: value to write - */ - -static void -forte_ac97_write (struct ac97_codec *codec, u8 reg, u16 val) -{ - struct forte_chip *chip = codec->private_data; - - spin_lock (&chip->ac97_lock); - - /* Knock, knock */ - if (forte_ac97_wait (chip)) { - printk (KERN_ERR PFX "ac97_write: Serial bus busy\n"); - goto out; - } - - outw (val, chip->iobase + FORTE_AC97_DATA); - outb (reg | FORTE_AC97_WRITE, chip->iobase + FORTE_AC97_CMD); - - /* Wait for completion */ - if (forte_ac97_wait (chip)) { - printk (KERN_ERR PFX "ac97_write: Bus busy after write\n"); - goto out; - } - - out: - spin_unlock (&chip->ac97_lock); -} - - -/* Mixer ------------------------------------------------------------------- */ - - -/** - * forte_mixer_open: - * @inode: - * @file: - */ - -static int -forte_mixer_open (struct inode *inode, struct file *file) -{ - struct forte_chip *chip = forte; - file->private_data = chip->ac97; - return 0; -} - - -/** - * forte_mixer_release: - * @inode: - * @file: - */ - -static int -forte_mixer_release (struct inode *inode, struct file *file) -{ - /* We will welease Wodewick */ - return 0; -} - - -/** - * forte_mixer_ioctl: - * @inode: - * @file: - */ - -static int -forte_mixer_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ac97_codec *codec = (struct ac97_codec *) file->private_data; - - return codec->mixer_ioctl (codec, cmd, arg); -} - - -static struct file_operations forte_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = forte_mixer_ioctl, - .open = forte_mixer_open, - .release = forte_mixer_release, -}; - - -/* Channel ----------------------------------------------------------------- */ - -/** - * forte_channel_reset: - * @channel: Channel to reset - * - * Locking: Must be called with lock held. - */ - -static void -forte_channel_reset (struct forte_channel *channel) -{ - if (!channel || !channel->iobase) - return; - - DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name); - - channel->ctrl &= ~FORTE_START; - outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); - - /* We always play at least two fragments, hence these defaults */ - channel->hwptr = channel->frag_sz; - channel->next_buf = 1; - channel->swptr = 0; - channel->filled_frags = 0; - channel->active = 0; - channel->bytes = 0; - channel->nr_irqs = 0; - channel->mapped = 0; - channel->residue = 0; -} - - -/** - * forte_channel_start: - * @channel: Channel to start (record/playback) - * - * Locking: Must be called with lock held. - */ - -static void inline -forte_channel_start (struct forte_channel *channel) -{ - if (!channel || !channel->iobase || channel->active) - return; - - channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST - | FORTE_IMMED_STOP); - channel->ctrl |= FORTE_START; - channel->active = 1; - outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); -} - - -/** - * forte_channel_stop: - * @channel: Channel to stop - * - * Locking: Must be called with lock held. - */ - -static void inline -forte_channel_stop (struct forte_channel *channel) -{ - if (!channel || !channel->iobase) - return; - - channel->ctrl &= ~(FORTE_START | FORTE_PAUSE); - channel->ctrl |= FORTE_IMMED_STOP; - - channel->active = 0; - outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); -} - - -/** - * forte_channel_pause: - * @channel: Channel to pause - * - * Locking: Must be called with lock held. - */ - -static void inline -forte_channel_pause (struct forte_channel *channel) -{ - if (!channel || !channel->iobase) - return; - - channel->ctrl |= FORTE_PAUSE; - - channel->active = 0; - outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); -} - - -/** - * forte_channel_rate: - * @channel: Channel whose rate to set. Playback and record are - * independent. - * @rate: Channel rate in Hz - * - * Locking: Must be called with lock held. - */ - -static int -forte_channel_rate (struct forte_channel *channel, unsigned int rate) -{ - int new_rate; - - if (!channel || !channel->iobase) - return -EINVAL; - - /* The FM801 only supports a handful of fixed frequencies. - * We find the value closest to what userland requested. - */ - if (rate <= 6250) { rate = 5500; new_rate = 0; } - else if (rate <= 8800) { rate = 8000; new_rate = 1; } - else if (rate <= 10312) { rate = 9600; new_rate = 2; } - else if (rate <= 13512) { rate = 11025; new_rate = 3; } - else if (rate <= 17600) { rate = 16000; new_rate = 4; } - else if (rate <= 20625) { rate = 19200; new_rate = 5; } - else if (rate <= 27025) { rate = 22050; new_rate = 6; } - else if (rate <= 35200) { rate = 32000; new_rate = 7; } - else if (rate <= 41250) { rate = 38400; new_rate = 8; } - else if (rate <= 46050) { rate = 44100; new_rate = 9; } - else { rate = 48000; new_rate = 10; } - - channel->ctrl &= ~FORTE_RATE_MASK; - channel->ctrl |= new_rate << FORTE_RATE_SHIFT; - channel->rate = rate; - - DPRINTK ("%s: %s rate = %d\n", __FUNCTION__, channel->name, rate); - - return rate; -} - - -/** - * forte_channel_format: - * @channel: Channel whose audio format to set - * @format: OSS format ID - * - * Locking: Must be called with lock held. - */ - -static int -forte_channel_format (struct forte_channel *channel, int format) -{ - if (!channel || !channel->iobase) - return -EINVAL; - - switch (format) { - - case AFMT_QUERY: - break; - - case AFMT_U8: - channel->ctrl &= ~FORTE_16BIT; - channel->format = AFMT_U8; - break; - - case AFMT_S16_LE: - default: - channel->ctrl |= FORTE_16BIT; - channel->format = AFMT_S16_LE; - break; - } - - DPRINTK ("%s: %s want %d format, got %d\n", __FUNCTION__, channel->name, - format, channel->format); - - return channel->format; -} - - -/** - * forte_channel_stereo: - * @channel: Channel to toggle - * @stereo: 0 for Mono, 1 for Stereo - * - * Locking: Must be called with lock held. - */ - -static int -forte_channel_stereo (struct forte_channel *channel, unsigned int stereo) -{ - int ret; - - if (!channel || !channel->iobase) - return -EINVAL; - - DPRINTK ("%s: %s stereo = %d\n", __FUNCTION__, channel->name, stereo); - - switch (stereo) { - - case 0: - channel->ctrl &= ~(FORTE_STEREO | FORTE_CHANNELS_MASK); - channel-> stereo = stereo; - ret = stereo; - break; - - case 1: - channel->ctrl &= ~FORTE_CHANNELS_MASK; - channel->ctrl |= FORTE_STEREO; - channel-> stereo = stereo; - ret = stereo; - break; - - default: - DPRINTK ("Unsupported channel format"); - ret = -EINVAL; - break; - } - - return ret; -} - - -/** - * forte_channel_buffer: - * @channel: Channel whose buffer to set up - * - * Locking: Must be called with lock held. - */ - -static void -forte_channel_buffer (struct forte_channel *channel, int sz, int num) -{ - unsigned int msecs, shift; - - /* Go away, I'm busy */ - if (channel->filled_frags || channel->bytes) - return; - - /* Fragment size must be a power of 2 */ - shift = 0; sz++; - while (sz >>= 1) - shift++; - channel->frag_sz = 1 << shift; - - /* Round fragment size to something reasonable */ - if (channel->frag_sz < FORTE_MIN_FRAG_SIZE) - channel->frag_sz = FORTE_MIN_FRAG_SIZE; - - if (channel->frag_sz > FORTE_MAX_FRAG_SIZE) - channel->frag_sz = FORTE_MAX_FRAG_SIZE; - - /* Find fragment length in milliseconds */ - msecs = channel->frag_sz / - (channel->format == AFMT_S16_LE ? 2 : 1) / - (channel->stereo ? 2 : 1) / - (channel->rate / 1000); - - channel->frag_msecs = msecs; - - /* Pick a suitable number of fragments */ - if (msecs * num < FORTE_MIN_BUF_MSECS) - num = FORTE_MIN_BUF_MSECS / msecs; - - if (msecs * num > FORTE_MAX_BUF_MSECS) - num = FORTE_MAX_BUF_MSECS / msecs; - - /* Fragment number must be a power of 2 */ - shift = 0; - while (num >>= 1) - shift++; - channel->frag_num = 1 << (shift + 1); - - /* Round fragment number to something reasonable */ - if (channel->frag_num < FORTE_MIN_FRAGMENTS) - channel->frag_num = FORTE_MIN_FRAGMENTS; - - if (channel->frag_num > FORTE_MAX_FRAGMENTS) - channel->frag_num = FORTE_MAX_FRAGMENTS; - - channel->buf_sz = channel->frag_sz * channel->frag_num; - - DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n", - __FUNCTION__, channel->name, channel->frag_sz, - channel->frag_num, channel->buf_sz); -} - - -/** - * forte_channel_prep: - * @channel: Channel whose buffer to prepare - * - * Locking: Lock held. - */ - -static void -forte_channel_prep (struct forte_channel *channel) -{ - struct page *page; - int i; - - if (channel->buf) - return; - - forte_channel_buffer (channel, channel->frag_sz, channel->frag_num); - channel->buf_pages = channel->buf_sz >> PAGE_SHIFT; - - if (channel->buf_sz % PAGE_SIZE) - channel->buf_pages++; - - DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d, pg = %d\n", - __FUNCTION__, channel->name, channel->frag_sz, - channel->frag_num, channel->buf_sz, channel->buf_pages); - - /* DMA buffer */ - channel->buf = pci_alloc_consistent (forte->pci_dev, - channel->buf_pages * PAGE_SIZE, - &channel->buf_handle); - - if (!channel->buf || !channel->buf_handle) - BUG(); - - page = virt_to_page (channel->buf); - - /* FIXME: can this go away ? */ - for (i = 0 ; i < channel->buf_pages ; i++) - SetPageReserved(page++); - - /* Prep buffer registers */ - outw (channel->frag_sz - 1, channel->iobase + FORTE_PLY_COUNT); - outl (channel->buf_handle, channel->iobase + FORTE_PLY_BUF1); - outl (channel->buf_handle + channel->frag_sz, - channel->iobase + FORTE_PLY_BUF2); - - /* Reset hwptr */ - channel->hwptr = channel->frag_sz; - channel->next_buf = 1; - - DPRINTK ("%s: %s buffer @ %p (%p)\n", __FUNCTION__, channel->name, - channel->buf, channel->buf_handle); -} - - -/** - * forte_channel_drain: - * @chip: - * @channel: - * - * Locking: Don't hold the lock. - */ - -static inline int -forte_channel_drain (struct forte_channel *channel) -{ - DECLARE_WAITQUEUE (wait, current); - unsigned long flags; - - DPRINTK ("%s\n", __FUNCTION__); - - if (channel->mapped) { - spin_lock_irqsave (&forte->lock, flags); - forte_channel_stop (channel); - spin_unlock_irqrestore (&forte->lock, flags); - return 0; - } - - spin_lock_irqsave (&forte->lock, flags); - add_wait_queue (&channel->wait, &wait); - - for (;;) { - if (channel->active == 0 || channel->filled_frags == 1) - break; - - spin_unlock_irqrestore (&forte->lock, flags); - - __set_current_state (TASK_INTERRUPTIBLE); - schedule(); - - spin_lock_irqsave (&forte->lock, flags); - } - - forte_channel_stop (channel); - forte_channel_reset (channel); - set_current_state (TASK_RUNNING); - remove_wait_queue (&channel->wait, &wait); - spin_unlock_irqrestore (&forte->lock, flags); - - return 0; -} - - -/** - * forte_channel_init: - * @chip: Forte chip instance the channel hangs off - * @channel: Channel to initialize - * - * Description: - * Initializes a channel, sets defaults, and allocates - * buffers. - * - * Locking: No lock held. - */ - -static int -forte_channel_init (struct forte_chip *chip, struct forte_channel *channel) -{ - DPRINTK ("%s: chip iobase @ %p\n", __FUNCTION__, (void *)chip->iobase); - - spin_lock_irq (&chip->lock); - memset (channel, 0x0, sizeof (*channel)); - - if (channel == &chip->play) { - channel->name = "PCM_OUT"; - channel->iobase = chip->iobase; - DPRINTK ("%s: PCM-OUT iobase @ %p\n", __FUNCTION__, - (void *) channel->iobase); - } - else if (channel == &chip->rec) { - channel->name = "PCM_IN"; - channel->iobase = chip->iobase + FORTE_CAP_OFFSET; - channel->record = 1; - DPRINTK ("%s: PCM-IN iobase @ %p\n", __FUNCTION__, - (void *) channel->iobase); - } - else - BUG(); - - init_waitqueue_head (&channel->wait); - - /* Defaults: 48kHz, 16-bit, stereo */ - channel->ctrl = inw (channel->iobase + FORTE_PLY_CTRL); - forte_channel_reset (channel); - forte_channel_stereo (channel, 1); - forte_channel_format (channel, AFMT_S16_LE); - forte_channel_rate (channel, 48000); - channel->frag_sz = FORTE_DEF_FRAG_SIZE; - channel->frag_num = FORTE_DEF_FRAGMENTS; - - chip->trigger = 0; - spin_unlock_irq (&chip->lock); - - return 0; -} - - -/** - * forte_channel_free: - * @chip: Chip this channel hangs off - * @channel: Channel to nuke - * - * Description: - * Resets channel and frees buffers. - * - * Locking: Hold your horses. - */ - -static void -forte_channel_free (struct forte_chip *chip, struct forte_channel *channel) -{ - DPRINTK ("%s: %s\n", __FUNCTION__, channel->name); - - if (!channel->buf_handle) - return; - - pci_free_consistent (chip->pci_dev, channel->buf_pages * PAGE_SIZE, - channel->buf, channel->buf_handle); - - memset (channel, 0x0, sizeof (*channel)); -} - - -/* DSP --------------------------------------------------------------------- */ - - -/** - * forte_dsp_ioctl: - */ - -static int -forte_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ival=0, ret, rval=0, rd, wr, count; - struct forte_chip *chip; - struct audio_buf_info abi; - struct count_info cinfo; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - chip = file->private_data; - - if (file->f_mode & FMODE_WRITE) - wr = 1; - else - wr = 0; - - if (file->f_mode & FMODE_READ) - rd = 1; - else - rd = 0; - - switch (cmd) { - - case OSS_GETVERSION: - return put_user (SOUND_VERSION, p); - - case SNDCTL_DSP_GETCAPS: - DPRINTK ("%s: GETCAPS\n", __FUNCTION__); - - ival = FORTE_CAPS; /* DUPLEX */ - return put_user (ival, p); - - case SNDCTL_DSP_GETFMTS: - DPRINTK ("%s: GETFMTS\n", __FUNCTION__); - - ival = FORTE_FMTS; /* U8, 16LE */ - return put_user (ival, p); - - case SNDCTL_DSP_SETFMT: /* U8, 16LE */ - DPRINTK ("%s: SETFMT\n", __FUNCTION__); - - if (get_user (ival, p)) - return -EFAULT; - - spin_lock_irq (&chip->lock); - - if (rd) { - forte_channel_stop (&chip->rec); - rval = forte_channel_format (&chip->rec, ival); - } - - if (wr) { - forte_channel_stop (&chip->rec); - rval = forte_channel_format (&chip->play, ival); - } - - spin_unlock_irq (&chip->lock); - - return put_user (rval, p); - - case SNDCTL_DSP_STEREO: /* 0 - mono, 1 - stereo */ - DPRINTK ("%s: STEREO\n", __FUNCTION__); - - if (get_user (ival, p)) - return -EFAULT; - - spin_lock_irq (&chip->lock); - - if (rd) { - forte_channel_stop (&chip->rec); - rval = forte_channel_stereo (&chip->rec, ival); - } - - if (wr) { - forte_channel_stop (&chip->rec); - rval = forte_channel_stereo (&chip->play, ival); - } - - spin_unlock_irq (&chip->lock); - - return put_user (rval, p); - - case SNDCTL_DSP_CHANNELS: /* 1 - mono, 2 - stereo */ - DPRINTK ("%s: CHANNELS\n", __FUNCTION__); - - if (get_user (ival, p)) - return -EFAULT; - - spin_lock_irq (&chip->lock); - - if (rd) { - forte_channel_stop (&chip->rec); - rval = forte_channel_stereo (&chip->rec, ival-1) + 1; - } - - if (wr) { - forte_channel_stop (&chip->play); - rval = forte_channel_stereo (&chip->play, ival-1) + 1; - } - - spin_unlock_irq (&chip->lock); - - return put_user (rval, p); - - case SNDCTL_DSP_SPEED: - DPRINTK ("%s: SPEED\n", __FUNCTION__); - - if (get_user (ival, p)) - return -EFAULT; - - spin_lock_irq (&chip->lock); - - if (rd) { - forte_channel_stop (&chip->rec); - rval = forte_channel_rate (&chip->rec, ival); - } - - if (wr) { - forte_channel_stop (&chip->play); - rval = forte_channel_rate (&chip->play, ival); - } - - spin_unlock_irq (&chip->lock); - - return put_user(rval, p); - - case SNDCTL_DSP_GETBLKSIZE: - DPRINTK ("%s: GETBLKSIZE\n", __FUNCTION__); - - spin_lock_irq (&chip->lock); - - if (rd) - ival = chip->rec.frag_sz; - - if (wr) - ival = chip->play.frag_sz; - - spin_unlock_irq (&chip->lock); - - return put_user (ival, p); - - case SNDCTL_DSP_RESET: - DPRINTK ("%s: RESET\n", __FUNCTION__); - - spin_lock_irq (&chip->lock); - - if (rd) - forte_channel_reset (&chip->rec); - - if (wr) - forte_channel_reset (&chip->play); - - spin_unlock_irq (&chip->lock); - - return 0; - - case SNDCTL_DSP_SYNC: - DPRINTK ("%s: SYNC\n", __FUNCTION__); - - if (wr) - ret = forte_channel_drain (&chip->play); - - return 0; - - case SNDCTL_DSP_POST: - DPRINTK ("%s: POST\n", __FUNCTION__); - - if (wr) { - spin_lock_irq (&chip->lock); - - if (chip->play.filled_frags) - forte_channel_start (&chip->play); - - spin_unlock_irq (&chip->lock); - } - - return 0; - - case SNDCTL_DSP_SETFRAGMENT: - DPRINTK ("%s: SETFRAGMENT\n", __FUNCTION__); - - if (get_user (ival, p)) - return -EFAULT; - - spin_lock_irq (&chip->lock); - - if (rd) { - forte_channel_buffer (&chip->rec, ival & 0xffff, - (ival >> 16) & 0xffff); - ival = (chip->rec.frag_num << 16) + chip->rec.frag_sz; - } - - if (wr) { - forte_channel_buffer (&chip->play, ival & 0xffff, - (ival >> 16) & 0xffff); - ival = (chip->play.frag_num << 16) +chip->play.frag_sz; - } - - spin_unlock_irq (&chip->lock); - - return put_user (ival, p); - - case SNDCTL_DSP_GETISPACE: - DPRINTK ("%s: GETISPACE\n", __FUNCTION__); - - if (!rd) - return -EINVAL; - - spin_lock_irq (&chip->lock); - - abi.fragstotal = chip->rec.frag_num; - abi.fragsize = chip->rec.frag_sz; - - if (chip->rec.mapped) { - abi.fragments = chip->rec.frag_num - 2; - abi.bytes = abi.fragments * abi.fragsize; - } - else { - abi.fragments = chip->rec.filled_frags; - abi.bytes = abi.fragments * abi.fragsize; - } - - spin_unlock_irq (&chip->lock); - - return copy_to_user (argp, &abi, sizeof (abi)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETIPTR: - DPRINTK ("%s: GETIPTR\n", __FUNCTION__); - - if (!rd) - return -EINVAL; - - spin_lock_irq (&chip->lock); - - if (chip->rec.active) - cinfo.ptr = chip->rec.hwptr; - else - cinfo.ptr = 0; - - cinfo.bytes = chip->rec.bytes; - cinfo.blocks = chip->rec.nr_irqs; - chip->rec.nr_irqs = 0; - - spin_unlock_irq (&chip->lock); - - return copy_to_user (argp, &cinfo, sizeof (cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOSPACE: - if (!wr) - return -EINVAL; - - spin_lock_irq (&chip->lock); - - abi.fragstotal = chip->play.frag_num; - abi.fragsize = chip->play.frag_sz; - - if (chip->play.mapped) { - abi.fragments = chip->play.frag_num - 2; - abi.bytes = chip->play.buf_sz; - } - else { - abi.fragments = chip->play.frag_num - - chip->play.filled_frags; - - if (chip->play.residue) - abi.fragments--; - - abi.bytes = abi.fragments * abi.fragsize + - chip->play.residue; - } - - spin_unlock_irq (&chip->lock); - - return copy_to_user (argp, &abi, sizeof (abi)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOPTR: - if (!wr) - return -EINVAL; - - spin_lock_irq (&chip->lock); - - if (chip->play.active) - cinfo.ptr = chip->play.hwptr; - else - cinfo.ptr = 0; - - cinfo.bytes = chip->play.bytes; - cinfo.blocks = chip->play.nr_irqs; - chip->play.nr_irqs = 0; - - spin_unlock_irq (&chip->lock); - - return copy_to_user (argp, &cinfo, sizeof (cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETODELAY: - if (!wr) - return -EINVAL; - - spin_lock_irq (&chip->lock); - - if (!chip->play.active) { - ival = 0; - } - else if (chip->play.mapped) { - count = inw (chip->play.iobase + FORTE_PLY_COUNT) + 1; - ival = chip->play.frag_sz - count; - } - else { - ival = chip->play.filled_frags * chip->play.frag_sz; - - if (chip->play.residue) - ival += chip->play.frag_sz - chip->play.residue; - } - - spin_unlock_irq (&chip->lock); - - return put_user (ival, p); - - case SNDCTL_DSP_SETDUPLEX: - DPRINTK ("%s: SETDUPLEX\n", __FUNCTION__); - - return -EINVAL; - - case SNDCTL_DSP_GETTRIGGER: - DPRINTK ("%s: GETTRIGGER\n", __FUNCTION__); - - return put_user (chip->trigger, p); - - case SNDCTL_DSP_SETTRIGGER: - - if (get_user (ival, p)) - return -EFAULT; - - DPRINTK ("%s: SETTRIGGER %d\n", __FUNCTION__, ival); - - if (wr) { - spin_lock_irq (&chip->lock); - - if (ival & PCM_ENABLE_OUTPUT) - forte_channel_start (&chip->play); - else { - chip->trigger = 1; - forte_channel_prep (&chip->play); - forte_channel_stop (&chip->play); - } - - spin_unlock_irq (&chip->lock); - } - else if (rd) { - spin_lock_irq (&chip->lock); - - if (ival & PCM_ENABLE_INPUT) - forte_channel_start (&chip->rec); - else { - chip->trigger = 1; - forte_channel_prep (&chip->rec); - forte_channel_stop (&chip->rec); - } - - spin_unlock_irq (&chip->lock); - } - - return 0; - - case SOUND_PCM_READ_RATE: - DPRINTK ("%s: PCM_READ_RATE\n", __FUNCTION__); - return put_user (chip->play.rate, p); - - case SOUND_PCM_READ_CHANNELS: - DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__); - return put_user (chip->play.stereo, p); - - case SOUND_PCM_READ_BITS: - DPRINTK ("%s: PCM_READ_BITS\n", __FUNCTION__); - return put_user (chip->play.format, p); - - case SNDCTL_DSP_NONBLOCK: - DPRINTK ("%s: DSP_NONBLOCK\n", __FUNCTION__); - file->f_flags |= O_NONBLOCK; - return 0; - - default: - DPRINTK ("Unsupported ioctl: %x (%p)\n", cmd, argp); - break; - } - - return -EINVAL; -} - - -/** - * forte_dsp_open: - */ - -static int -forte_dsp_open (struct inode *inode, struct file *file) -{ - struct forte_chip *chip = forte; /* FIXME: HACK FROM HELL! */ - - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&chip->open_mutex)) { - DPRINTK ("%s: returning -EAGAIN\n", __FUNCTION__); - return -EAGAIN; - } - } - else { - if (mutex_lock_interruptible(&chip->open_mutex)) { - DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__); - return -ERESTARTSYS; - } - } - - file->private_data = forte; - - DPRINTK ("%s: dsp opened by %d\n", __FUNCTION__, current->pid); - - if (file->f_mode & FMODE_WRITE) - forte_channel_init (forte, &forte->play); - - if (file->f_mode & FMODE_READ) - forte_channel_init (forte, &forte->rec); - - return nonseekable_open(inode, file); -} - - -/** - * forte_dsp_release: - */ - -static int -forte_dsp_release (struct inode *inode, struct file *file) -{ - struct forte_chip *chip = file->private_data; - int ret = 0; - - DPRINTK ("%s: chip @ %p\n", __FUNCTION__, chip); - - if (file->f_mode & FMODE_WRITE) { - forte_channel_drain (&chip->play); - - spin_lock_irq (&chip->lock); - - forte_channel_free (chip, &chip->play); - - spin_unlock_irq (&chip->lock); - } - - if (file->f_mode & FMODE_READ) { - while (chip->rec.filled_frags > 0) - interruptible_sleep_on (&chip->rec.wait); - - spin_lock_irq (&chip->lock); - - forte_channel_stop (&chip->rec); - forte_channel_free (chip, &chip->rec); - - spin_unlock_irq (&chip->lock); - } - - mutex_unlock(&chip->open_mutex); - - return ret; -} - - -/** - * forte_dsp_poll: - * - */ - -static unsigned int -forte_dsp_poll (struct file *file, struct poll_table_struct *wait) -{ - struct forte_chip *chip; - struct forte_channel *channel; - unsigned int mask = 0; - - chip = file->private_data; - - if (file->f_mode & FMODE_WRITE) { - channel = &chip->play; - - if (channel->active) - poll_wait (file, &channel->wait, wait); - - spin_lock_irq (&chip->lock); - - if (channel->frag_num - channel->filled_frags > 0) - mask |= POLLOUT | POLLWRNORM; - - spin_unlock_irq (&chip->lock); - } - - if (file->f_mode & FMODE_READ) { - channel = &chip->rec; - - if (channel->active) - poll_wait (file, &channel->wait, wait); - - spin_lock_irq (&chip->lock); - - if (channel->filled_frags > 0) - mask |= POLLIN | POLLRDNORM; - - spin_unlock_irq (&chip->lock); - } - - return mask; -} - - -/** - * forte_dsp_mmap: - */ - -static int -forte_dsp_mmap (struct file *file, struct vm_area_struct *vma) -{ - struct forte_chip *chip; - struct forte_channel *channel; - unsigned long size; - int ret; - - chip = file->private_data; - - DPRINTK ("%s: start %lXh, size %ld, pgoff %ld\n", __FUNCTION__, - vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_pgoff); - - spin_lock_irq (&chip->lock); - - if (vma->vm_flags & VM_WRITE && chip->play.active) { - ret = -EBUSY; - goto out; - } - - if (vma->vm_flags & VM_READ && chip->rec.active) { - ret = -EBUSY; - goto out; - } - - if (file->f_mode & FMODE_WRITE) - channel = &chip->play; - else if (file->f_mode & FMODE_READ) - channel = &chip->rec; - else { - ret = -EINVAL; - goto out; - } - - forte_channel_prep (channel); - channel->mapped = 1; - - if (vma->vm_pgoff != 0) { - ret = -EINVAL; - goto out; - } - - size = vma->vm_end - vma->vm_start; - - if (size > channel->buf_pages * PAGE_SIZE) { - DPRINTK ("%s: size (%ld) > buf_sz (%d) \n", __FUNCTION__, - size, channel->buf_sz); - ret = -EINVAL; - goto out; - } - - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(channel->buf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) { - DPRINTK ("%s: remap el a no worko\n", __FUNCTION__); - ret = -EAGAIN; - goto out; - } - - ret = 0; - - out: - spin_unlock_irq (&chip->lock); - return ret; -} - - -/** - * forte_dsp_write: - */ - -static ssize_t -forte_dsp_write (struct file *file, const char __user *buffer, size_t bytes, - loff_t *ppos) -{ - struct forte_chip *chip; - struct forte_channel *channel; - unsigned int i = bytes, sz = 0; - unsigned long flags; - - if (!access_ok (VERIFY_READ, buffer, bytes)) - return -EFAULT; - - chip = (struct forte_chip *) file->private_data; - - if (!chip) - BUG(); - - channel = &chip->play; - - if (!channel) - BUG(); - - spin_lock_irqsave (&chip->lock, flags); - - /* Set up buffers with the right fragment size */ - forte_channel_prep (channel); - - while (i) { - /* All fragment buffers in use -> wait */ - if (channel->frag_num - channel->filled_frags == 0) { - DECLARE_WAITQUEUE (wait, current); - - /* For trigger or non-blocking operation, get out */ - if (chip->trigger || file->f_flags & O_NONBLOCK) { - spin_unlock_irqrestore (&chip->lock, flags); - return -EAGAIN; - } - - /* Otherwise wait for buffers */ - add_wait_queue (&channel->wait, &wait); - - for (;;) { - spin_unlock_irqrestore (&chip->lock, flags); - - set_current_state (TASK_INTERRUPTIBLE); - schedule(); - - spin_lock_irqsave (&chip->lock, flags); - - if (channel->frag_num - channel->filled_frags) - break; - } - - remove_wait_queue (&channel->wait, &wait); - set_current_state (TASK_RUNNING); - - if (signal_pending (current)) { - spin_unlock_irqrestore (&chip->lock, flags); - return -ERESTARTSYS; - } - } - - if (channel->residue) - sz = channel->residue; - else if (i > channel->frag_sz) - sz = channel->frag_sz; - else - sz = i; - - spin_unlock_irqrestore (&chip->lock, flags); - - if (copy_from_user ((void *) channel->buf + channel->swptr, buffer, sz)) - return -EFAULT; - - spin_lock_irqsave (&chip->lock, flags); - - /* Advance software pointer */ - buffer += sz; - channel->swptr += sz; - channel->swptr %= channel->buf_sz; - i -= sz; - - /* Only bump filled_frags if a full fragment has been written */ - if (channel->swptr % channel->frag_sz == 0) { - channel->filled_frags++; - channel->residue = 0; - } - else - channel->residue = channel->frag_sz - sz; - - /* If playback isn't active, start it */ - if (channel->active == 0 && chip->trigger == 0) - forte_channel_start (channel); - } - - spin_unlock_irqrestore (&chip->lock, flags); - - return bytes - i; -} - - -/** - * forte_dsp_read: - */ - -static ssize_t -forte_dsp_read (struct file *file, char __user *buffer, size_t bytes, - loff_t *ppos) -{ - struct forte_chip *chip; - struct forte_channel *channel; - unsigned int i = bytes, sz; - unsigned long flags; - - if (!access_ok (VERIFY_WRITE, buffer, bytes)) - return -EFAULT; - - chip = (struct forte_chip *) file->private_data; - - if (!chip) - BUG(); - - channel = &chip->rec; - - if (!channel) - BUG(); - - spin_lock_irqsave (&chip->lock, flags); - - /* Set up buffers with the right fragment size */ - forte_channel_prep (channel); - - /* Start recording */ - if (!chip->trigger) - forte_channel_start (channel); - - while (i) { - /* No fragment buffers in use -> wait */ - if (channel->filled_frags == 0) { - DECLARE_WAITQUEUE (wait, current); - - /* For trigger mode operation, get out */ - if (chip->trigger) { - spin_unlock_irqrestore (&chip->lock, flags); - return -EAGAIN; - } - - add_wait_queue (&channel->wait, &wait); - - for (;;) { - if (channel->active == 0) - break; - - if (channel->filled_frags) - break; - - spin_unlock_irqrestore (&chip->lock, flags); - - set_current_state (TASK_INTERRUPTIBLE); - schedule(); - - spin_lock_irqsave (&chip->lock, flags); - } - - set_current_state (TASK_RUNNING); - remove_wait_queue (&channel->wait, &wait); - } - - if (i > channel->frag_sz) - sz = channel->frag_sz; - else - sz = i; - - spin_unlock_irqrestore (&chip->lock, flags); - - if (copy_to_user (buffer, (void *)channel->buf+channel->swptr, sz)) { - DPRINTK ("%s: copy_to_user failed\n", __FUNCTION__); - return -EFAULT; - } - - spin_lock_irqsave (&chip->lock, flags); - - /* Advance software pointer */ - buffer += sz; - if (channel->filled_frags > 0) - channel->filled_frags--; - channel->swptr += channel->frag_sz; - channel->swptr %= channel->buf_sz; - i -= sz; - } - - spin_unlock_irqrestore (&chip->lock, flags); - - return bytes - i; -} - - -static struct file_operations forte_dsp_fops = { - .owner = THIS_MODULE, - .llseek = &no_llseek, - .read = &forte_dsp_read, - .write = &forte_dsp_write, - .poll = &forte_dsp_poll, - .ioctl = &forte_dsp_ioctl, - .open = &forte_dsp_open, - .release = &forte_dsp_release, - .mmap = &forte_dsp_mmap, -}; - - -/* Common ------------------------------------------------------------------ */ - - -/** - * forte_interrupt: - */ - -static irqreturn_t -forte_interrupt (int irq, void *dev_id, struct pt_regs *regs) -{ - struct forte_chip *chip = dev_id; - struct forte_channel *channel = NULL; - u16 status, count; - - status = inw (chip->iobase + FORTE_IRQ_STATUS); - - /* If this is not for us, get outta here ASAP */ - if ((status & (FORTE_IRQ_PLAYBACK | FORTE_IRQ_CAPTURE)) == 0) - return IRQ_NONE; - - if (status & FORTE_IRQ_PLAYBACK) { - channel = &chip->play; - - spin_lock (&chip->lock); - - if (channel->frag_sz == 0) - goto pack; - - /* Declare a fragment done */ - if (channel->filled_frags > 0) - channel->filled_frags--; - channel->bytes += channel->frag_sz; - channel->nr_irqs++; - - /* Flip-flop between buffer I and II */ - channel->next_buf ^= 1; - - /* Advance hardware pointer by fragment size and wrap around */ - channel->hwptr += channel->frag_sz; - channel->hwptr %= channel->buf_sz; - - /* Buffer I or buffer II BAR */ - outl (channel->buf_handle + channel->hwptr, - channel->next_buf == 0 ? - channel->iobase + FORTE_PLY_BUF1 : - channel->iobase + FORTE_PLY_BUF2); - - /* If the currently playing fragment is last, schedule pause */ - if (channel->filled_frags == 1) - forte_channel_pause (channel); - - pack: - /* Acknowledge interrupt */ - outw (FORTE_IRQ_PLAYBACK, chip->iobase + FORTE_IRQ_STATUS); - - if (waitqueue_active (&channel->wait)) - wake_up_all (&channel->wait); - - spin_unlock (&chip->lock); - } - - if (status & FORTE_IRQ_CAPTURE) { - channel = &chip->rec; - spin_lock (&chip->lock); - - /* One fragment filled */ - channel->filled_frags++; - - /* Get # of completed bytes */ - count = inw (channel->iobase + FORTE_PLY_COUNT) + 1; - - if (count == 0) { - DPRINTK ("%s: last, filled_frags = %d\n", __FUNCTION__, - channel->filled_frags); - channel->filled_frags = 0; - goto rack; - } - - /* Buffer I or buffer II BAR */ - outl (channel->buf_handle + channel->hwptr, - channel->next_buf == 0 ? - channel->iobase + FORTE_PLY_BUF1 : - channel->iobase + FORTE_PLY_BUF2); - - /* Flip-flop between buffer I and II */ - channel->next_buf ^= 1; - - /* Advance hardware pointer by fragment size and wrap around */ - channel->hwptr += channel->frag_sz; - channel->hwptr %= channel->buf_sz; - - /* Out of buffers */ - if (channel->filled_frags == channel->frag_num - 1) - forte_channel_stop (channel); - rack: - /* Acknowledge interrupt */ - outw (FORTE_IRQ_CAPTURE, chip->iobase + FORTE_IRQ_STATUS); - - spin_unlock (&chip->lock); - - if (waitqueue_active (&channel->wait)) - wake_up_all (&channel->wait); - } - - return IRQ_HANDLED; -} - - -/** - * forte_proc_read: - */ - -static int -forte_proc_read (char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int i = 0, p_rate, p_chan, r_rate; - unsigned short p_reg, r_reg; - - i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n \n", - DRIVER_VERSION); - - if (!forte->iobase) - return i; - - p_rate = p_chan = -1; - p_reg = inw (forte->iobase + FORTE_PLY_CTRL); - p_rate = (p_reg >> 8) & 15; - p_chan = (p_reg >> 12) & 3; - - if (p_rate >= 0 || p_rate <= 10) - p_rate = rates[p_rate]; - - if (p_chan >= 0 || p_chan <= 2) - p_chan = channels[p_chan]; - - r_rate = -1; - r_reg = inw (forte->iobase + FORTE_CAP_CTRL); - r_rate = (r_reg >> 8) & 15; - - if (r_rate >= 0 || r_rate <= 10) - r_rate = rates[r_rate]; - - i += sprintf (page + i, - " Playback Capture\n" - "FIFO empty : %-3s %-3s\n" - "Buf1 Last : %-3s %-3s\n" - "Buf2 Last : %-3s %-3s\n" - "Started : %-3s %-3s\n" - "Paused : %-3s %-3s\n" - "Immed Stop : %-3s %-3s\n" - "Rate : %-5d %-5d\n" - "Channels : %-5d -\n" - "16-bit : %-3s %-3s\n" - "Stereo : %-3s %-3s\n" - " \n" - "Buffer Sz : %-6d %-6d\n" - "Frag Sz : %-6d %-6d\n" - "Frag Num : %-6d %-6d\n" - "Frag msecs : %-6d %-6d\n" - "Used Frags : %-6d %-6d\n" - "Mapped : %-3s %-3s\n", - p_reg & 1<<0 ? "yes" : "no", - r_reg & 1<<0 ? "yes" : "no", - p_reg & 1<<1 ? "yes" : "no", - r_reg & 1<<1 ? "yes" : "no", - p_reg & 1<<2 ? "yes" : "no", - r_reg & 1<<2 ? "yes" : "no", - p_reg & 1<<5 ? "yes" : "no", - r_reg & 1<<5 ? "yes" : "no", - p_reg & 1<<6 ? "yes" : "no", - r_reg & 1<<6 ? "yes" : "no", - p_reg & 1<<7 ? "yes" : "no", - r_reg & 1<<7 ? "yes" : "no", - p_rate, r_rate, - p_chan, - p_reg & 1<<14 ? "yes" : "no", - r_reg & 1<<14 ? "yes" : "no", - p_reg & 1<<15 ? "yes" : "no", - r_reg & 1<<15 ? "yes" : "no", - forte->play.buf_sz, forte->rec.buf_sz, - forte->play.frag_sz, forte->rec.frag_sz, - forte->play.frag_num, forte->rec.frag_num, - forte->play.frag_msecs, forte->rec.frag_msecs, - forte->play.filled_frags, forte->rec.filled_frags, - forte->play.mapped ? "yes" : "no", - forte->rec.mapped ? "yes" : "no" - ); - - return i; -} - - -/** - * forte_proc_init: - * - * Creates driver info entries in /proc - */ - -static int __init -forte_proc_init (void) -{ - if (!proc_mkdir ("driver/forte", NULL)) - return -EIO; - - if (!create_proc_read_entry ("driver/forte/chip", 0, NULL, forte_proc_read, forte)) { - remove_proc_entry ("driver/forte", NULL); - return -EIO; - } - - if (!create_proc_read_entry("driver/forte/ac97", 0, NULL, ac97_read_proc, forte->ac97)) { - remove_proc_entry ("driver/forte/chip", NULL); - remove_proc_entry ("driver/forte", NULL); - return -EIO; - } - - return 0; -} - - -/** - * forte_proc_remove: - * - * Removes driver info entries in /proc - */ - -static void -forte_proc_remove (void) -{ - remove_proc_entry ("driver/forte/ac97", NULL); - remove_proc_entry ("driver/forte/chip", NULL); - remove_proc_entry ("driver/forte", NULL); -} - - -/** - * forte_chip_init: - * @chip: Chip instance to initialize - * - * Description: - * Resets chip, configures codec and registers the driver with - * the sound subsystem. - * - * Press and hold Start for 8 secs, then switch on Run - * and hold for 4 seconds. Let go of Start. Numbers - * assume a properly oiled TWG. - */ - -static int __devinit -forte_chip_init (struct forte_chip *chip) -{ - u8 revision; - u16 cmdw; - struct ac97_codec *codec; - - pci_read_config_byte (chip->pci_dev, PCI_REVISION_ID, &revision); - - if (revision >= 0xB1) { - chip->multichannel = 1; - printk (KERN_INFO PFX "Multi-channel device detected.\n"); - } - - /* Reset chip */ - outw (FORTE_CC_CODEC_RESET | FORTE_CC_AC97_RESET, - chip->iobase + FORTE_CODEC_CTRL); - udelay(100); - outw (0, chip->iobase + FORTE_CODEC_CTRL); - - /* Request read from AC97 */ - outw (FORTE_AC97_READ | (0 << FORTE_AC97_ADDR_SHIFT), - chip->iobase + FORTE_AC97_CMD); - mdelay(750); - - if ((inw (chip->iobase + FORTE_AC97_CMD) & (3<<8)) != (1<<8)) { - printk (KERN_INFO PFX "AC97 codec not responding"); - return -EIO; - } - - /* Init volume */ - outw (0x0808, chip->iobase + FORTE_PCM_VOL); - outw (0x9f1f, chip->iobase + FORTE_FM_VOL); - outw (0x8808, chip->iobase + FORTE_I2S_VOL); - - /* I2S control - I2S mode */ - outw (0x0003, chip->iobase + FORTE_I2S_MODE); - - /* Interrupt setup - unmask PLAYBACK & CAPTURE */ - cmdw = inw (chip->iobase + FORTE_IRQ_MASK); - cmdw &= ~0x0003; - outw (cmdw, chip->iobase + FORTE_IRQ_MASK); - - /* Interrupt clear */ - outw (FORTE_IRQ_PLAYBACK|FORTE_IRQ_CAPTURE, - chip->iobase + FORTE_IRQ_STATUS); - - /* Set up the AC97 codec */ - if ((codec = ac97_alloc_codec()) == NULL) - return -ENOMEM; - codec->private_data = chip; - codec->codec_read = forte_ac97_read; - codec->codec_write = forte_ac97_write; - codec->id = 0; - - if (ac97_probe_codec (codec) == 0) { - printk (KERN_ERR PFX "codec probe failed\n"); - ac97_release_codec(codec); - return -1; - } - - /* Register mixer */ - if ((codec->dev_mixer = - register_sound_mixer (&forte_mixer_fops, -1)) < 0) { - printk (KERN_ERR PFX "couldn't register mixer!\n"); - ac97_release_codec(codec); - return -1; - } - - chip->ac97 = codec; - - /* Register DSP */ - if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) { - printk (KERN_ERR PFX "couldn't register dsp!\n"); - return -1; - } - - /* Register with /proc */ - if (forte_proc_init()) { - printk (KERN_ERR PFX "couldn't add entries to /proc!\n"); - return -1; - } - - return 0; -} - - -/** - * forte_probe: - * @pci_dev: PCI struct for probed device - * @pci_id: - * - * Description: - * Allocates chip instance, I/O region, and IRQ - */ -static int __init -forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id) -{ - struct forte_chip *chip; - int ret = 0; - - /* FIXME: Support more than one chip */ - if (found++) - return -EIO; - - /* Ignition */ - if (pci_enable_device (pci_dev)) - return -EIO; - - pci_set_master (pci_dev); - - /* Allocate chip instance and configure */ - forte = (struct forte_chip *) - kmalloc (sizeof (struct forte_chip), GFP_KERNEL); - chip = forte; - - if (chip == NULL) { - printk (KERN_WARNING PFX "Out of memory"); - return -ENOMEM; - } - - memset (chip, 0, sizeof (struct forte_chip)); - chip->pci_dev = pci_dev; - - mutex_init(&chip->open_mutex); - spin_lock_init (&chip->lock); - spin_lock_init (&chip->ac97_lock); - - if (! request_region (pci_resource_start (pci_dev, 0), - pci_resource_len (pci_dev, 0), DRIVER_NAME)) { - printk (KERN_WARNING PFX "Unable to reserve I/O space"); - ret = -ENOMEM; - goto error; - } - - chip->iobase = pci_resource_start (pci_dev, 0); - chip->irq = pci_dev->irq; - - if (request_irq (chip->irq, forte_interrupt, IRQF_SHARED, DRIVER_NAME, - chip)) { - printk (KERN_WARNING PFX "Unable to reserve IRQ"); - ret = -EIO; - goto error; - } - - pci_set_drvdata (pci_dev, chip); - - printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%16llX IRQ %u\n", - chip->iobase, (unsigned long long)pci_resource_end (pci_dev, 0), - chip->irq); - - /* Power it up */ - if ((ret = forte_chip_init (chip)) == 0) - return 0; - - error: - if (chip->irq) - free_irq (chip->irq, chip); - - if (chip->iobase) - release_region (pci_resource_start (pci_dev, 0), - pci_resource_len (pci_dev, 0)); - - kfree (chip); - - return ret; -} - - -/** - * forte_remove: - * @pci_dev: PCI device to unclaim - * - */ - -static void -forte_remove (struct pci_dev *pci_dev) -{ - struct forte_chip *chip = pci_get_drvdata (pci_dev); - - if (chip == NULL) - return; - - /* Turn volume down to avoid popping */ - outw (0x1f1f, chip->iobase + FORTE_PCM_VOL); - outw (0x1f1f, chip->iobase + FORTE_FM_VOL); - outw (0x1f1f, chip->iobase + FORTE_I2S_VOL); - - forte_proc_remove(); - free_irq (chip->irq, chip); - release_region (chip->iobase, pci_resource_len (pci_dev, 0)); - - unregister_sound_dsp (chip->dsp); - unregister_sound_mixer (chip->ac97->dev_mixer); - ac97_release_codec(chip->ac97); - kfree (chip); - - printk (KERN_INFO PFX "driver released\n"); -} - - -static struct pci_device_id forte_pci_ids[] = { - { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { 0, } -}; - - -static struct pci_driver forte_pci_driver = { - .name = DRIVER_NAME, - .id_table = forte_pci_ids, - .probe = forte_probe, - .remove = forte_remove, - -}; - - -/** - * forte_init_module: - * - */ - -static int __init -forte_init_module (void) -{ - printk (KERN_INFO PFX DRIVER_VERSION "\n"); - - return pci_register_driver (&forte_pci_driver); -} - - -/** - * forte_cleanup_module: - * - */ - -static void __exit -forte_cleanup_module (void) -{ - pci_unregister_driver (&forte_pci_driver); -} - - -module_init(forte_init_module); -module_exit(forte_cleanup_module); - -MODULE_AUTHOR("Martin K. Petersen "); -MODULE_DESCRIPTION("ForteMedia FM801 OSS Driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE (pci, forte_pci_ids); diff --git a/sound/oss/gus.h b/sound/oss/gus.h deleted file mode 100644 index 3d5271baf0..0000000000 --- a/sound/oss/gus.h +++ /dev/null @@ -1,24 +0,0 @@ - -#include "ad1848.h" - -/* From gus_card.c */ -int gus_set_midi_irq(int num); -irqreturn_t gusintr(int irq, void *dev_id, struct pt_regs * dummy); - -/* From gus_wave.c */ -int gus_wave_detect(int baseaddr); -void gus_wave_init(struct address_info *hw_config); -void gus_wave_unload (struct address_info *hw_config); -void gus_voice_irq(void); -void gus_write8(int reg, unsigned int data); -void guswave_dma_irq(void); -void gus_delay(void); -int gus_default_mixer_ioctl (int dev, unsigned int cmd, void __user *arg); -void gus_timer_command (unsigned int addr, unsigned int val); - -/* From gus_midi.c */ -void gus_midi_init(struct address_info *hw_config); -void gus_midi_interrupt(int dummy); - -/* From ics2101.c */ -int ics2101_mixer_init(void); diff --git a/sound/oss/gus_card.c b/sound/oss/gus_card.c deleted file mode 100644 index a3d6ae33fe..0000000000 --- a/sound/oss/gus_card.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * sound/oss/gus_card.c - * - * Detection routine for the Gravis Ultrasound. - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * - * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious - * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. - * Christoph Hellwig: Adapted to module_init/module_exit, simple cleanups. - * - * Status: - * Tested... - */ - - -#include -#include -#include -#include - -#include "sound_config.h" - -#include "gus.h" -#include "gus_hw.h" - -irqreturn_t gusintr(int irq, void *dev_id, struct pt_regs *dummy); - -int gus_base = 0, gus_irq = 0, gus_dma = 0; -int gus_no_wave_dma = 0; -extern int gus_wave_volume; -extern int gus_pcm_volume; -extern int have_gus_max; -int gus_pnp_flag = 0; -#ifdef CONFIG_SOUND_GUS16 -static int db16; /* Has a Gus16 AD1848 on it */ -#endif - -static void __init attach_gus(struct address_info *hw_config) -{ - gus_wave_init(hw_config); - - if (sound_alloc_dma(hw_config->dma, "GUS")) - printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma); - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) - printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); - gus_midi_init(hw_config); - if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) - printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); - - return; -} - -static int __init probe_gus(struct address_info *hw_config) -{ - int irq; - int io_addr; - - if (hw_config->card_subtype == 1) - gus_pnp_flag = 1; - - irq = hw_config->irq; - - if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ - if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && - irq != 11 && irq != 12 && irq != 15) - { - printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq); - return 0; - } - if (gus_wave_detect(hw_config->io_base)) - return 1; - -#ifndef EXCLUDE_GUS_IODETECT - - /* - * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) - */ - - for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) { - if (io_addr == hw_config->io_base) /* Already tested */ - continue; - if (gus_wave_detect(io_addr)) { - hw_config->io_base = io_addr; - return 1; - } - } -#endif - - printk("NO GUS card found !\n"); - return 0; -} - -static void __exit unload_gus(struct address_info *hw_config) -{ - DDB(printk("unload_gus(%x)\n", hw_config->io_base)); - - gus_wave_unload(hw_config); - - release_region(hw_config->io_base, 16); - release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ - free_irq(hw_config->irq, hw_config); - - sound_free_dma(hw_config->dma); - - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - sound_free_dma(hw_config->dma2); -} - -irqreturn_t gusintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - unsigned char src; - extern int gus_timer_enabled; - int handled = 0; - -#ifdef CONFIG_SOUND_GUSMAX - if (have_gus_max) { - struct address_info *hw_config = dev_id; - adintr(irq, (void *)hw_config->slots[1], NULL); - } -#endif -#ifdef CONFIG_SOUND_GUS16 - if (db16) { - struct address_info *hw_config = dev_id; - adintr(irq, (void *)hw_config->slots[3], NULL); - } -#endif - - while (1) - { - if (!(src = inb(u_IrqStatus))) - break; - handled = 1; - if (src & DMA_TC_IRQ) - { - guswave_dma_irq(); - } - if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) - { - gus_midi_interrupt(0); - } - if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) - { - if (gus_timer_enabled) - sound_timer_interrupt(); - gus_write8(0x45, 0); /* Ack IRQ */ - gus_timer_command(4, 0x80); /* Reset IRQ flags */ - } - if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) - gus_voice_irq(); - } - return IRQ_RETVAL(handled); -} - -/* - * Some extra code for the 16 bit sampling option - */ - -#ifdef CONFIG_SOUND_GUS16 - -static int __init init_gus_db16(struct address_info *hw_config) -{ - struct resource *ports; - - ports = request_region(hw_config->io_base, 4, "ad1848"); - if (!ports) - return 0; - - if (!ad1848_detect(ports, NULL, hw_config->osp)) { - release_region(hw_config->io_base, 4); - return 0; - } - - gus_pcm_volume = 100; - gus_wave_volume = 90; - - hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", ports, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0, - hw_config->osp, - THIS_MODULE); - return 1; -} - -static void __exit unload_gus_db16(struct address_info *hw_config) -{ - - ad1848_unload(hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0); - sound_unload_audiodev(hw_config->slots[3]); -} -#endif - -#ifdef CONFIG_SOUND_GUS16 -static int gus16; -#endif -#ifdef CONFIG_SOUND_GUSMAX -static int no_wave_dma; /* Set if no dma is to be used for the - wave table (GF1 chip) */ -#endif - - -/* - * Note DMA2 of -1 has the right meaning in the GUS driver as well - * as here. - */ - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata type = 0; /* 1 for PnP */ - -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(dma, int, 0); -module_param(dma16, int, 0); -module_param(type, int, 0); -#ifdef CONFIG_SOUND_GUSMAX -module_param(no_wave_dma, int, 0); -#endif -#ifdef CONFIG_SOUND_GUS16 -module_param(db16, int, 0); -module_param(gus16, int, 0); -#endif -MODULE_LICENSE("GPL"); - -static int __init init_gus(void) -{ - printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - cfg.card_subtype = type; -#ifdef CONFIG_SOUND_GUSMAX - gus_no_wave_dma = no_wave_dma; -#endif - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); - return -EINVAL; - } - -#ifdef CONFIG_SOUND_GUS16 - if (gus16 && init_gus_db16(&cfg)) - db16 = 1; -#endif - if (!probe_gus(&cfg)) - return -ENODEV; - attach_gus(&cfg); - - return 0; -} - -static void __exit cleanup_gus(void) -{ -#ifdef CONFIG_SOUND_GUS16 - if (db16) - unload_gus_db16(&cfg); -#endif - unload_gus(&cfg); -} - -module_init(init_gus); -module_exit(cleanup_gus); - -#ifndef MODULE -static int __init setup_gus(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - - return 1; -} - -__setup("gus=", setup_gus); -#endif diff --git a/sound/oss/gus_hw.h b/sound/oss/gus_hw.h deleted file mode 100644 index f97a0b8670..0000000000 --- a/sound/oss/gus_hw.h +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * I/O addresses - */ - -#define u_Base (gus_base + 0x000) -#define u_Mixer u_Base -#define u_Status (gus_base + 0x006) -#define u_TimerControl (gus_base + 0x008) -#define u_TimerData (gus_base + 0x009) -#define u_IRQDMAControl (gus_base + 0x00b) -#define u_MidiControl (gus_base + 0x100) -#define MIDI_RESET 0x03 -#define MIDI_ENABLE_XMIT 0x20 -#define MIDI_ENABLE_RCV 0x80 -#define u_MidiStatus u_MidiControl -#define MIDI_RCV_FULL 0x01 -#define MIDI_XMIT_EMPTY 0x02 -#define MIDI_FRAME_ERR 0x10 -#define MIDI_OVERRUN 0x20 -#define MIDI_IRQ_PEND 0x80 -#define u_MidiData (gus_base + 0x101) -#define u_Voice (gus_base + 0x102) -#define u_Command (gus_base + 0x103) -#define u_DataLo (gus_base + 0x104) -#define u_DataHi (gus_base + 0x105) -#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ -#define u_MixSelect (gus_base + 0x506) /* registers. */ -#define u_IrqStatus u_Status -# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ -# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ -# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ -# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ -# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ -# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ -# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ - -#define ICS2101 1 -# define ICS_MIXDEVS 6 -# define DEV_MIC 0 -# define DEV_LINE 1 -# define DEV_CD 2 -# define DEV_GF1 3 -# define DEV_UNUSED 4 -# define DEV_VOL 5 - -# define CHN_LEFT 0 -# define CHN_RIGHT 1 -#define CS4231 2 -#define u_DRAMIO (gus_base + 0x107) diff --git a/sound/oss/gus_linearvol.h b/sound/oss/gus_linearvol.h deleted file mode 100644 index 7ad0c30d4f..0000000000 --- a/sound/oss/gus_linearvol.h +++ /dev/null @@ -1,18 +0,0 @@ -static unsigned short gus_linearvol[128] = { - 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, - 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, - 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, - 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, - 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, - 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, - 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, - 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, - 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, - 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, - 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, - 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, - 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, - 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, - 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, - 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc -}; diff --git a/sound/oss/gus_midi.c b/sound/oss/gus_midi.c deleted file mode 100644 index d1997a417a..0000000000 --- a/sound/oss/gus_midi.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * sound/oss/gus_midi.c - * - * The low level driver for the GUS Midi Interface. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added __init to gus_midi_init() - */ - -#include -#include -#include "sound_config.h" - -#include "gus.h" -#include "gus_hw.h" - -static int midi_busy, input_opened; -static int my_dev; -static int output_used; -static volatile unsigned char gus_midi_control; -static void (*midi_input_intr) (int dev, unsigned char data); - -static unsigned char tmp_queue[256]; -extern int gus_pnp_flag; -static volatile int qlen; -static volatile unsigned char qhead, qtail; -extern int gus_base, gus_irq, gus_dma; -extern int *gus_osp; -extern spinlock_t gus_lock; - -static int GUS_MIDI_STATUS(void) -{ - return inb(u_MidiStatus); -} - -static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)) -{ - if (midi_busy) - { -/* printk("GUS: Midi busy\n");*/ - return -EBUSY; - } - outb((MIDI_RESET), u_MidiControl); - gus_delay(); - - gus_midi_control = 0; - input_opened = 0; - - if (mode == OPEN_READ || mode == OPEN_READWRITE) - if (!gus_pnp_flag) - { - gus_midi_control |= MIDI_ENABLE_RCV; - input_opened = 1; - } - outb((gus_midi_control), u_MidiControl); /* Enable */ - - midi_busy = 1; - qlen = qhead = qtail = output_used = 0; - midi_input_intr = input; - - return 0; -} - -static int dump_to_midi(unsigned char midi_byte) -{ - unsigned long flags; - int ok = 0; - - output_used = 1; - - spin_lock_irqsave(&gus_lock, flags); - - if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) - { - ok = 1; - outb((midi_byte), u_MidiData); - } - else - { - /* - * Enable Midi xmit interrupts (again) - */ - gus_midi_control |= MIDI_ENABLE_XMIT; - outb((gus_midi_control), u_MidiControl); - } - - spin_unlock_irqrestore(&gus_lock,flags); - return ok; -} - -static void gus_midi_close(int dev) -{ - /* - * Reset FIFO pointers, disable intrs - */ - - outb((MIDI_RESET), u_MidiControl); - midi_busy = 0; -} - -static int gus_midi_out(int dev, unsigned char midi_byte) -{ - unsigned long flags; - - /* - * Drain the local queue first - */ - spin_lock_irqsave(&gus_lock, flags); - - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - spin_unlock_irqrestore(&gus_lock,flags); - - /* - * Output the byte if the local queue is empty. - */ - - if (!qlen) - if (dump_to_midi(midi_byte)) - return 1; /* - * OK - */ - - /* - * Put to the local queue - */ - - if (qlen >= 256) - return 0; /* - * Local queue full - */ - spin_lock_irqsave(&gus_lock, flags); - - tmp_queue[qtail] = midi_byte; - qlen++; - qtail++; - - spin_unlock_irqrestore(&gus_lock,flags); - return 1; -} - -static int gus_midi_start_read(int dev) -{ - return 0; -} - -static int gus_midi_end_read(int dev) -{ - return 0; -} - -static void gus_midi_kick(int dev) -{ -} - -static int gus_midi_buffer_status(int dev) -{ - unsigned long flags; - - if (!output_used) - return 0; - - spin_lock_irqsave(&gus_lock, flags); - - if (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - spin_unlock_irqrestore(&gus_lock,flags); - return (qlen > 0) || !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); -} - -#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct midi_operations gus_midi_operations = -{ - .owner = THIS_MODULE, - .info = {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = gus_midi_open, - .close = gus_midi_close, - .outputc = gus_midi_out, - .start_read = gus_midi_start_read, - .end_read = gus_midi_end_read, - .kick = gus_midi_kick, - .buffer_status = gus_midi_buffer_status, -}; - -void __init gus_midi_init(struct address_info *hw_config) -{ - int dev = sound_alloc_mididev(); - - if (dev == -1) - { - printk(KERN_INFO "gus_midi: Too many midi devices detected\n"); - return; - } - outb((MIDI_RESET), u_MidiControl); - - std_midi_synth.midi_dev = my_dev = dev; - hw_config->slots[2] = dev; - midi_devs[dev] = &gus_midi_operations; - sequencer_init(); - return; -} - -void gus_midi_interrupt(int dummy) -{ - volatile unsigned char stat, data; - int timeout = 10; - - spin_lock(&gus_lock); - - while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) - { - if (stat & MIDI_RCV_FULL) - { - data = inb(u_MidiData); - if (input_opened) - midi_input_intr(my_dev, data); - } - if (stat & MIDI_XMIT_EMPTY) - { - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - if (!qlen) - { - /* - * Disable Midi output interrupts, since no data in the buffer - */ - gus_midi_control &= ~MIDI_ENABLE_XMIT; - outb((gus_midi_control), u_MidiControl); - outb((gus_midi_control), u_MidiControl); - } - } - } - spin_unlock(&gus_lock); -} diff --git a/sound/oss/gus_vol.c b/sound/oss/gus_vol.c deleted file mode 100644 index 6ae6924e16..0000000000 --- a/sound/oss/gus_vol.c +++ /dev/null @@ -1,153 +0,0 @@ - -/* - * gus_vol.c - Compute volume for GUS. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -#include "sound_config.h" - -#include "gus.h" -#include "gus_linearvol.h" - -#define GUS_VOLUME gus_wave_volume - - -extern int gus_wave_volume; - -/* - * Calculate gus volume from note velocity, main volume, expression, and - * intrinsic patch volume given in patch library. Expression is multiplied - * in, so it emphasizes differences in note velocity, while main volume is - * added in -- I don't know whether this is right, but it seems reasonable to - * me. (In the previous stage, main volume controller messages were changed - * to expression controller messages, if they were found to be used for - * dynamic volume adjustments, so here, main volume can be assumed to be - * constant throughout a song.) - * - * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so - * we can give a big boost to very weak voices like nylon guitar and the - * basses. The normal value is 64. Strings are assigned lower values. - */ - -unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev) -{ - int i, m, n, x; - - - /* - * A voice volume of 64 is considered neutral, so adjust the main volume if - * something other than this neutral value was assigned in the patch - * library. - */ - x = 256 + 6 * (voicev - 64); - - /* - * Boost expression by voice volume above neutral. - */ - - if (voicev > 65) - xpn += voicev - 64; - xpn += (voicev - 64) / 2; - - /* - * Combine multiplicative and level components. - */ - x = vel * xpn * 6 + (voicev / 4) * x; - -#ifdef GUS_VOLUME - /* - * Further adjustment by installation-specific master volume control - * (default 60). - */ - x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; -#endif - -#ifdef GUS_USE_CHN_MAIN_VOLUME - /* - * Experimental support for the channel main volume - */ - - mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ - x = (x * mainv * mainv) / 16384; -#endif - - if (x < 2) - return (0); - else if (x >= 65535) - return ((15 << 8) | 255); - - /* - * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit - * mantissa m. - */ - - n = x; - i = 7; - if (n < 128) - { - while (i > 0 && n < (1 << i)) - i--; - } - else - { - while (n > 255) - { - n >>= 1; - i++; - } - } - /* - * Mantissa is part of linear volume not expressed in exponent. (This is - * not quite like real logs -- I wonder if it's right.) - */ - m = x - (1 << i); - - /* - * Adjust mantissa to 8 bits. - */ - if (m > 0) - { - if (i > 8) - m >>= i - 8; - else if (i < 8) - m <<= 8 - i; - } - return ((i << 8) + m); -} - -/* - * Volume-values are interpreted as linear values. Volume is based on the - * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) - * and the volume set by the mixer-device (default 60%). - */ - -unsigned short gus_linear_vol(int vol, int mainvol) -{ - int mixer_mainvol; - - if (vol <= 0) - vol = 0; - else if (vol >= 127) - vol = 127; - -#ifdef GUS_VOLUME - mixer_mainvol = GUS_VOLUME; -#else - mixer_mainvol = 100; -#endif - -#ifdef GUS_USE_CHN_MAIN_VOLUME - if (mainvol <= 0) - mainvol = 0; - else if (mainvol >= 127) - mainvol = 127; -#else - mainvol = 127; -#endif - return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; -} diff --git a/sound/oss/gus_wave.c b/sound/oss/gus_wave.c deleted file mode 100644 index 597db7aee6..0000000000 --- a/sound/oss/gus_wave.c +++ /dev/null @@ -1,3464 +0,0 @@ -/* - * sound/oss/gus_wave.c - * - * Driver for the Gravis UltraSound wave table synth. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious - * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. - * Bartlomiej Zolnierkiewicz : added some __init/__exit - */ - -#include -#include -#include - -#define GUSPNP_AUTODETECT - -#include "sound_config.h" -#include - -#include "gus.h" -#include "gus_hw.h" - -#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) - -#define MAX_SAMPLE 150 -#define MAX_PATCH 256 - -#define NOT_SAMPLE 0xffff - -struct voice_info -{ - unsigned long orig_freq; - unsigned long current_freq; - unsigned long mode; - int fixed_pitch; - int bender; - int bender_range; - int panning; - int midi_volume; - unsigned int initial_volume; - unsigned int current_volume; - int loop_irq_mode, loop_irq_parm; -#define LMODE_FINISH 1 -#define LMODE_PCM 2 -#define LMODE_PCM_STOP 3 - int volume_irq_mode, volume_irq_parm; -#define VMODE_HALT 1 -#define VMODE_ENVELOPE 2 -#define VMODE_START_NOTE 3 - - int env_phase; - unsigned char env_rate[6]; - unsigned char env_offset[6]; - - /* - * Volume computation parameters for gus_adagio_vol() - */ - int main_vol, expression_vol, patch_vol; - - /* Variables for "Ultraclick" removal */ - int dev_pending, note_pending, volume_pending, - sample_pending; - char kill_pending; - long offset_pending; - -}; - -static struct voice_alloc_info *voice_alloc; -static struct address_info *gus_hw_config; -extern int gus_base; -extern int gus_irq, gus_dma; -extern int gus_pnp_flag; -extern int gus_no_wave_dma; -static int gus_dma2 = -1; -static int dual_dma_mode; -static long gus_mem_size; -static long free_mem_ptr; -static int gus_busy; -static int gus_no_dma; -static int nr_voices; -static int gus_devnum; -static int volume_base, volume_scale, volume_method; -static int gus_recmask = SOUND_MASK_MIC; -static int recording_active; -static int only_read_access; -static int only_8_bits; - -static int iw_mode = 0; -int gus_wave_volume = 60; -int gus_pcm_volume = 80; -int have_gus_max = 0; -static int gus_line_vol = 100, gus_mic_vol; -static unsigned char mix_image = 0x00; - -int gus_timer_enabled = 0; - -/* - * Current version of this driver doesn't allow synth and PCM functions - * at the same time. The active_device specifies the active driver - */ - -static int active_device; - -#define GUS_DEV_WAVE 1 /* Wave table synth */ -#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ -#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ - -static int gus_audio_speed; -static int gus_audio_channels; -static int gus_audio_bits; -static int gus_audio_bsize; -static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */ - -static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper); - -/* - * Variables and buffers for PCM output - */ - -#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */ - -static int pcm_bsize, pcm_nblk, pcm_banksize; -static int pcm_datasize[MAX_PCM_BUFFERS]; -static volatile int pcm_head, pcm_tail, pcm_qlen; -static volatile int pcm_active; -static volatile int dma_active; -static int pcm_opened; -static int pcm_current_dev; -static int pcm_current_block; -static unsigned long pcm_current_buf; -static int pcm_current_count; -static int pcm_current_intrflag; -DEFINE_SPINLOCK(gus_lock); - -extern int *gus_osp; - -static struct voice_info voices[32]; - -static int freq_div_table[] = -{ - 44100, /* 14 */ - 41160, /* 15 */ - 38587, /* 16 */ - 36317, /* 17 */ - 34300, /* 18 */ - 32494, /* 19 */ - 30870, /* 20 */ - 29400, /* 21 */ - 28063, /* 22 */ - 26843, /* 23 */ - 25725, /* 24 */ - 24696, /* 25 */ - 23746, /* 26 */ - 22866, /* 27 */ - 22050, /* 28 */ - 21289, /* 29 */ - 20580, /* 30 */ - 19916, /* 31 */ - 19293 /* 32 */ -}; - -static struct patch_info *samples; -static long sample_ptrs[MAX_SAMPLE + 1]; -static int sample_map[32]; -static int free_sample; -static int mixer_type; - - -static int patch_table[MAX_PATCH]; -static int patch_map[32]; - -static struct synth_info gus_info = { - "Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, - 0, 16, 0, MAX_PATCH -}; - -static void gus_poke(long addr, unsigned char data); -static void compute_and_set_volume(int voice, int volume, int ramp_time); -extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); -extern unsigned short gus_linear_vol(int vol, int mainvol); -static void compute_volume(int voice, int volume); -static void do_volume_irq(int voice); -static void set_input_volumes(void); -static void gus_tmr_install(int io_base); - -#define INSTANT_RAMP -1 /* Instant change. No ramping */ -#define FAST_RAMP 0 /* Fastest possible ramp */ - -static void reset_sample_memory(void) -{ - int i; - - for (i = 0; i <= MAX_SAMPLE; i++) - sample_ptrs[i] = -1; - for (i = 0; i < 32; i++) - sample_map[i] = -1; - for (i = 0; i < 32; i++) - patch_map[i] = -1; - - gus_poke(0, 0); /* Put a silent sample to the beginning */ - gus_poke(1, 0); - free_mem_ptr = 2; - - free_sample = 0; - - for (i = 0; i < MAX_PATCH; i++) - patch_table[i] = NOT_SAMPLE; -} - -void gus_delay(void) -{ - int i; - - for (i = 0; i < 7; i++) - inb(u_DRAMIO); -} - -static void gus_poke(long addr, unsigned char data) -{ /* Writes a byte to the DRAM */ - outb((0x43), u_Command); - outb((addr & 0xff), u_DataLo); - outb(((addr >> 8) & 0xff), u_DataHi); - - outb((0x44), u_Command); - outb(((addr >> 16) & 0xff), u_DataHi); - outb((data), u_DRAMIO); -} - -static unsigned char gus_peek(long addr) -{ /* Reads a byte from the DRAM */ - unsigned char tmp; - - outb((0x43), u_Command); - outb((addr & 0xff), u_DataLo); - outb(((addr >> 8) & 0xff), u_DataHi); - - outb((0x44), u_Command); - outb(((addr >> 16) & 0xff), u_DataHi); - tmp = inb(u_DRAMIO); - - return tmp; -} - -void gus_write8(int reg, unsigned int data) -{ /* Writes to an indirect register (8 bit) */ - outb((reg), u_Command); - outb(((unsigned char) (data & 0xff)), u_DataHi); -} - -static unsigned char gus_read8(int reg) -{ - /* Reads from an indirect register (8 bit). Offset 0x80. */ - unsigned char val; - - outb((reg | 0x80), u_Command); - val = inb(u_DataHi); - - return val; -} - -static unsigned char gus_look8(int reg) -{ - /* Reads from an indirect register (8 bit). No additional offset. */ - unsigned char val; - - outb((reg), u_Command); - val = inb(u_DataHi); - - return val; -} - -static void gus_write16(int reg, unsigned int data) -{ - /* Writes to an indirect register (16 bit) */ - outb((reg), u_Command); - - outb(((unsigned char) (data & 0xff)), u_DataLo); - outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); -} - -static unsigned short gus_read16(int reg) -{ - /* Reads from an indirect register (16 bit). Offset 0x80. */ - unsigned char hi, lo; - - outb((reg | 0x80), u_Command); - - lo = inb(u_DataLo); - hi = inb(u_DataHi); - - return ((hi << 8) & 0xff00) | lo; -} - -static unsigned short gus_look16(int reg) -{ - /* Reads from an indirect register (16 bit). No additional offset. */ - unsigned char hi, lo; - - outb((reg), u_Command); - - lo = inb(u_DataLo); - hi = inb(u_DataHi); - - return ((hi << 8) & 0xff00) | lo; -} - -static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit) -{ - /* Writes an 24 bit memory address */ - unsigned long hold_address; - - if (is16bit) - { - if (iw_mode) - { - /* Interwave spesific address translations */ - address >>= 1; - } - else - { - /* - * Special processing required for 16 bit patches - */ - - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - } - gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); - gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) - + (frac << 5)); - /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ - gus_delay(); - gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); - gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) - + (frac << 5)); -} - -static void gus_select_voice(int voice) -{ - if (voice < 0 || voice > 31) - return; - outb((voice), u_Voice); -} - -static void gus_select_max_voices(int nvoices) -{ - if (iw_mode) - nvoices = 32; - if (nvoices < 14) - nvoices = 14; - if (nvoices > 32) - nvoices = 32; - - voice_alloc->max_voice = nr_voices = nvoices; - gus_write8(0x0e, (nvoices - 1) | 0xc0); -} - -static void gus_voice_on(unsigned int mode) -{ - gus_write8(0x00, (unsigned char) (mode & 0xfc)); - gus_delay(); - gus_write8(0x00, (unsigned char) (mode & 0xfc)); -} - -static void gus_voice_off(void) -{ - gus_write8(0x00, gus_read8(0x00) | 0x03); -} - -static void gus_voice_mode(unsigned int m) -{ - unsigned char mode = (unsigned char) (m & 0xff); - - gus_write8(0x00, (gus_read8(0x00) & 0x03) | - (mode & 0xfc)); /* Don't touch last two bits */ - gus_delay(); - gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc)); -} - -static void gus_voice_freq(unsigned long freq) -{ - unsigned long divisor = freq_div_table[nr_voices - 14]; - unsigned short fc; - - /* Interwave plays at 44100 Hz with any number of voices */ - if (iw_mode) - fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); - else - fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); - fc = fc << 1; - - gus_write16(0x01, fc); -} - -static void gus_voice_volume(unsigned int vol) -{ - gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ - gus_write16(0x09, (unsigned short) (vol << 4)); -} - -static void gus_voice_balance(unsigned int balance) -{ - gus_write8(0x0c, (unsigned char) (balance & 0xff)); -} - -static void gus_ramp_range(unsigned int low, unsigned int high) -{ - gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff)); - gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff)); -} - -static void gus_ramp_rate(unsigned int scale, unsigned int rate) -{ - gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); -} - -static void gus_rampon(unsigned int m) -{ - unsigned char mode = (unsigned char) (m & 0xff); - - gus_write8(0x0d, mode & 0xfc); - gus_delay(); - gus_write8(0x0d, mode & 0xfc); -} - -static void gus_ramp_mode(unsigned int m) -{ - unsigned char mode = (unsigned char) (m & 0xff); - - gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | - (mode & 0xfc)); /* Leave the last 2 bits alone */ - gus_delay(); - gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc)); -} - -static void gus_rampoff(void) -{ - gus_write8(0x0d, 0x03); -} - -static void gus_set_voice_pos(int voice, long position) -{ - int sample_no; - - if ((sample_no = sample_map[voice]) != -1) { - if (position < samples[sample_no].len) { - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - voices[voice].offset_pending = position; - else - gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, - samples[sample_no].mode & WAVE_16_BITS); - } - } -} - -static void gus_voice_init(int voice) -{ - unsigned long flags; - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_volume(0); - gus_voice_off(); - gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ - gus_write8(0x00, 0x03); /* Voice off */ - gus_write8(0x0d, 0x03); /* Ramping off */ - voice_alloc->map[voice] = 0; - voice_alloc->alloc_times[voice] = 0; - spin_unlock_irqrestore(&gus_lock,flags); - -} - -static void gus_voice_init2(int voice) -{ - voices[voice].panning = 0; - voices[voice].mode = 0; - voices[voice].orig_freq = 20000; - voices[voice].current_freq = 20000; - voices[voice].bender = 0; - voices[voice].bender_range = 200; - voices[voice].initial_volume = 0; - voices[voice].current_volume = 0; - voices[voice].loop_irq_mode = 0; - voices[voice].loop_irq_parm = 0; - voices[voice].volume_irq_mode = 0; - voices[voice].volume_irq_parm = 0; - voices[voice].env_phase = 0; - voices[voice].main_vol = 127; - voices[voice].patch_vol = 127; - voices[voice].expression_vol = 127; - voices[voice].sample_pending = -1; - voices[voice].fixed_pitch = 0; -} - -static void step_envelope(int voice) -{ - unsigned vol, prev_vol, phase; - unsigned char rate; - unsigned long flags; - - if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) - { - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_rampoff(); - spin_unlock_irqrestore(&gus_lock,flags); - return; - /* - * Sustain phase begins. Continue envelope after receiving note off. - */ - } - if (voices[voice].env_phase >= 5) - { - /* Envelope finished. Shoot the voice down */ - gus_voice_init(voice); - return; - } - prev_vol = voices[voice].current_volume; - phase = ++voices[voice].env_phase; - compute_volume(voice, voices[voice].midi_volume); - vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; - rate = voices[voice].env_rate[phase]; - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - - gus_voice_volume(prev_vol); - - - gus_write8(0x06, rate); /* Ramping rate */ - - voices[voice].volume_irq_mode = VMODE_ENVELOPE; - - if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ - { - spin_unlock_irqrestore(&gus_lock,flags); - step_envelope(voice); /* Continue the envelope on the next step */ - return; - } - if (vol > prev_vol) - { - if (vol >= (4096 - 64)) - vol = 4096 - 65; - gus_ramp_range(0, vol); - gus_rampon(0x20); /* Increasing volume, with IRQ */ - } - else - { - if (vol <= 64) - vol = 65; - gus_ramp_range(vol, 4030); - gus_rampon(0x60); /* Decreasing volume, with IRQ */ - } - voices[voice].current_volume = vol; - spin_unlock_irqrestore(&gus_lock,flags); -} - -static void init_envelope(int voice) -{ - voices[voice].env_phase = -1; - voices[voice].current_volume = 64; - - step_envelope(voice); -} - -static void start_release(int voice) -{ - if (gus_read8(0x00) & 0x03) - return; /* Voice already stopped */ - - voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ - - voices[voice].current_volume = voices[voice].initial_volume = - gus_read16(0x09) >> 4; /* Get current volume */ - - voices[voice].mode &= ~WAVE_SUSTAIN_ON; - gus_rampoff(); - step_envelope(voice); -} - -static void gus_voice_fade(int voice) -{ - int instr_no = sample_map[voice], is16bits; - unsigned long flags; - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - - if (instr_no < 0 || instr_no > MAX_SAMPLE) - { - gus_write8(0x00, 0x03); /* Hard stop */ - voice_alloc->map[voice] = 0; - spin_unlock_irqrestore(&gus_lock,flags); - return; - } - is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ - - if (voices[voice].mode & WAVE_ENVELOPES) - { - start_release(voice); - spin_unlock_irqrestore(&gus_lock,flags); - return; - } - /* - * Ramp the volume down but not too quickly. - */ - if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */ - { - gus_voice_off(); - gus_rampoff(); - gus_voice_init(voice); - spin_unlock_irqrestore(&gus_lock,flags); - return; - } - gus_ramp_range(65, 4030); - gus_ramp_rate(2, 4); - gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */ - voices[voice].volume_irq_mode = VMODE_HALT; - spin_unlock_irqrestore(&gus_lock,flags); -} - -static void gus_reset(void) -{ - int i; - - gus_select_max_voices(24); - volume_base = 3071; - volume_scale = 4; - volume_method = VOL_METHOD_ADAGIO; - - for (i = 0; i < 32; i++) - { - gus_voice_init(i); /* Turn voice off */ - gus_voice_init2(i); - } -} - -static void gus_initialize(void) -{ - unsigned long flags; - unsigned char dma_image, irq_image, tmp; - - static unsigned char gus_irq_map[16] = { - 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 - }; - - static unsigned char gus_dma_map[8] = { - 0, 1, 0, 2, 0, 3, 4, 5 - }; - - spin_lock_irqsave(&gus_lock,flags); - gus_write8(0x4c, 0); /* Reset GF1 */ - gus_delay(); - gus_delay(); - - gus_write8(0x4c, 1); /* Release Reset */ - gus_delay(); - gus_delay(); - - /* - * Clear all interrupts - */ - - gus_write8(0x41, 0); /* DMA control */ - gus_write8(0x45, 0); /* Timer control */ - gus_write8(0x49, 0); /* Sample control */ - - gus_select_max_voices(24); - - inb(u_Status); /* Touch the status register */ - - gus_look8(0x41); /* Clear any pending DMA IRQs */ - gus_look8(0x49); /* Clear any pending sample IRQs */ - gus_read8(0x0f); /* Clear pending IRQs */ - - gus_reset(); /* Resets all voices */ - - gus_look8(0x41); /* Clear any pending DMA IRQs */ - gus_look8(0x49); /* Clear any pending sample IRQs */ - gus_read8(0x0f); /* Clear pending IRQs */ - - gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ - - /* - * Set up for Digital ASIC - */ - - outb((0x05), gus_base + 0x0f); - - mix_image |= 0x02; /* Disable line out (for a moment) */ - outb((mix_image), u_Mixer); - - outb((0x00), u_IRQDMAControl); - - outb((0x00), gus_base + 0x0f); - - /* - * Now set up the DMA and IRQ interface - * - * The GUS supports two IRQs and two DMAs. - * - * Just one DMA channel is used. This prevents simultaneous ADC and DAC. - * Adding this support requires significant changes to the dmabuf.c, dsp.c - * and audio.c also. - */ - - irq_image = 0; - tmp = gus_irq_map[gus_irq]; - if (!gus_pnp_flag && !tmp) - printk(KERN_WARNING "Warning! GUS IRQ not selected\n"); - irq_image |= tmp; - irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ - - dual_dma_mode = 1; - if (gus_dma2 == gus_dma || gus_dma2 == -1) - { - dual_dma_mode = 0; - dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ - - tmp = gus_dma_map[gus_dma]; - if (!tmp) - printk(KERN_WARNING "Warning! GUS DMA not selected\n"); - - dma_image |= tmp; - } - else - { - /* Setup dual DMA channel mode for GUS MAX */ - - dma_image = gus_dma_map[gus_dma]; - if (!dma_image) - printk(KERN_WARNING "Warning! GUS DMA not selected\n"); - - tmp = gus_dma_map[gus_dma2] << 3; - if (!tmp) - { - printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n"); - tmp = 0x40; /* Combine DMA channels */ - dual_dma_mode = 0; - } - dma_image |= tmp; - } - - /* - * For some reason the IRQ and DMA addresses must be written twice - */ - - /* - * Doing it first time - */ - - outb((mix_image), u_Mixer); /* Select DMA control */ - outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */ - - outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ - outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ - - /* - * Doing it second time - */ - - outb((mix_image), u_Mixer); /* Select DMA control */ - outb((dma_image), u_IRQDMAControl); /* Set DMA address */ - - outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ - outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ - - gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ - - mix_image &= ~0x02; /* Enable line out */ - mix_image |= 0x08; /* Enable IRQ */ - outb((mix_image), u_Mixer); /* - * Turn mixer channels on - * Note! Mic in is left off. - */ - - gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ - - gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - - inb(u_Status); /* Touch the status register */ - - gus_look8(0x41); /* Clear any pending DMA IRQs */ - gus_look8(0x49); /* Clear any pending sample IRQs */ - - gus_read8(0x0f); /* Clear pending IRQs */ - - if (iw_mode) - gus_write8(0x19, gus_read8(0x19) | 0x01); - spin_unlock_irqrestore(&gus_lock,flags); -} - - -static void __init pnp_mem_init(void) -{ -#include "iwmem.h" -#define CHUNK_SIZE (256*1024) -#define BANK_SIZE (4*1024*1024) -#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE) - - int bank, chunk, addr, total = 0; - int bank_sizes[4]; - int i, j, bits = -1, testbits = -1, nbanks = 0; - - /* - * This routine determines what kind of RAM is installed in each of the four - * SIMM banks and configures the DRAM address decode logic accordingly. - */ - - /* - * Place the chip into enhanced mode - */ - gus_write8(0x19, gus_read8(0x19) | 0x01); - gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */ - - /* - * Set memory configuration to 4 DRAM banks of 4M in each (16M total). - */ - - gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c); - - /* - * Perform the DRAM size detection for each bank individually. - */ - for (bank = 0; bank < 4; bank++) - { - int size = 0; - - addr = bank * BANK_SIZE; - - /* Clean check points of each chunk */ - for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) - { - gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); - gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); - } - - /* Write a value to each chunk point and verify the result */ - for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) - { - gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55); - gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA); - - if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 && - gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA) - { - /* OK. There is RAM. Now check for possible shadows */ - int ok = 1, chunk2; - - for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) - if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) || - gus_peek(addr + chunk2 * CHUNK_SIZE + 1L)) - ok = 0; /* Addressing wraps */ - - if (ok) - size = (chunk + 1) * CHUNK_SIZE; - } - gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); - gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); - } - bank_sizes[bank] = size; - if (size) - nbanks = bank + 1; - DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); - } - - if (nbanks == 0) /* No RAM - Give up */ - { - printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n"); - printk(KERN_ERR "Sound: Unable to work with this card.\n"); - gus_write8(0x19, gus_read8(0x19) & ~0x01); - gus_mem_size = 0; - return; - } - - /* - * Now we know how much DRAM there is in each bank. The next step is - * to find a DRAM size encoding (0 to 12) which is best for the combination - * we have. - * - * First try if any of the possible alternatives matches exactly the amount - * of memory we have. - */ - - for (i = 0; bits == -1 && i < 13; i++) - { - bits = i; - - for (j = 0; bits != -1 && j < 4; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ - } - - /* - * If necessary, try to find a combination where other than the last - * bank matches our configuration and the last bank is left oversized. - * In this way we don't leave holes in the middle of memory. - */ - - if (bits == -1) /* No luck yet */ - { - for (i = 0; bits == -1 && i < 13; i++) - { - bits = i; - - for (j = 0; bits != -1 && j < nbanks - 1; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ - if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) - bits = -1; /* The last bank is too small */ - } - } - /* - * The last resort is to search for a combination where the banks are - * smaller than the actual SIMMs. This leaves some memory in the banks - * unused but doesn't leave holes in the DRAM address space. - */ - if (bits == -1) /* No luck yet */ - { - for (i = 0; i < 13; i++) - { - testbits = i; - for (j = 0; testbits != -1 && j < nbanks - 1; j++) - if (mem_decode[i][j] > bank_sizes[j]) { - testbits = -1; - } - if(testbits > bits) bits = testbits; - } - if (bits != -1) - { - printk(KERN_INFO "Interwave: Can't use all installed RAM.\n"); - printk(KERN_INFO "Interwave: Try reordering SIMMS.\n"); - } - printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n"); - printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); - bits = 0; - } - DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits)); - - for (bank = 0; bank < 4; bank++) - { - DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); - - if (bank_sizes[bank] > mem_decode[bits][bank]) - total += mem_decode[bits][bank]; - else - total += bank_sizes[bank]; - } - - DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024)); - - /* - * Set the memory addressing mode. - */ - gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits); - -/* Leave the chip into enhanced mode. Disable LFO */ - gus_mem_size = total; - iw_mode = 1; - gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02); -} - -int __init gus_wave_detect(int baseaddr) -{ - unsigned long i, max_mem = 1024L; - unsigned long loc; - unsigned char val; - - if (!request_region(baseaddr, 16, "GUS")) - return 0; - if (!request_region(baseaddr + 0x100, 12, "GUS")) { /* 0x10c-> is MAX */ - release_region(baseaddr, 16); - return 0; - } - - gus_base = baseaddr; - - gus_write8(0x4c, 0); /* Reset GF1 */ - gus_delay(); - gus_delay(); - - gus_write8(0x4c, 1); /* Release Reset */ - gus_delay(); - gus_delay(); - -#ifdef GUSPNP_AUTODETECT - val = gus_look8(0x5b); /* Version number register */ - gus_write8(0x5b, ~val); /* Invert all bits */ - - if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */ - { - if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ - { - DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); - gus_pnp_flag = 1; - } - else - { - DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b))); - gus_pnp_flag = 0; - } - } - gus_write8(0x5b, val); /* Restore all bits */ -#endif - - if (gus_pnp_flag) - pnp_mem_init(); - if (iw_mode) - return 1; - - /* See if there is first block there.... */ - gus_poke(0L, 0xaa); - if (gus_peek(0L) != 0xaa) { - release_region(baseaddr + 0x100, 12); - release_region(baseaddr, 16); - return 0; - } - - /* Now zero it out so that I can check for mirroring .. */ - gus_poke(0L, 0x00); - for (i = 1L; i < max_mem; i++) - { - int n, failed; - - /* check for mirroring ... */ - if (gus_peek(0L) != 0) - break; - loc = i << 10; - - for (n = loc - 1, failed = 0; n <= loc; n++) - { - gus_poke(loc, 0xaa); - if (gus_peek(loc) != 0xaa) - failed = 1; - gus_poke(loc, 0x55); - if (gus_peek(loc) != 0x55) - failed = 1; - } - if (failed) - break; - } - gus_mem_size = i << 10; - return 1; -} - -static int guswave_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - - switch (cmd) - { - case SNDCTL_SYNTH_INFO: - gus_info.nr_voices = nr_voices; - if (copy_to_user(arg, &gus_info, sizeof(gus_info))) - return -EFAULT; - return 0; - - case SNDCTL_SEQ_RESETSAMPLES: - reset_sample_memory(); - return 0; - - case SNDCTL_SEQ_PERCMODE: - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32; - - default: - return -EINVAL; - } -} - -static int guswave_set_instr(int dev, int voice, int instr_no) -{ - int sample_no; - - if (instr_no < 0 || instr_no > MAX_PATCH) - instr_no = 0; /* Default to acoustic piano */ - - if (voice < 0 || voice > 31) - return -EINVAL; - - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].sample_pending = instr_no; - return 0; - } - sample_no = patch_table[instr_no]; - patch_map[voice] = -1; - - if (sample_no == NOT_SAMPLE) - { -/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ - return -EINVAL; /* Patch not defined */ - } - if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ - { -/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ - return -EINVAL; - } - sample_map[voice] = sample_no; - patch_map[voice] = instr_no; - return 0; -} - -static int guswave_kill_note(int dev, int voice, int note, int velocity) -{ - unsigned long flags; - - spin_lock_irqsave(&gus_lock,flags); - /* voice_alloc->map[voice] = 0xffff; */ - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].kill_pending = 1; - spin_unlock_irqrestore(&gus_lock,flags); - } - else - { - spin_unlock_irqrestore(&gus_lock,flags); - gus_voice_fade(voice); - } - - return 0; -} - -static void guswave_aftertouch(int dev, int voice, int pressure) -{ -} - -static void guswave_panning(int dev, int voice, int value) -{ - if (voice >= 0 || voice < 32) - voices[voice].panning = value; -} - -static void guswave_volume_method(int dev, int mode) -{ - if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) - volume_method = mode; -} - -static void compute_volume(int voice, int volume) -{ - if (volume < 128) - voices[voice].midi_volume = volume; - - switch (volume_method) - { - case VOL_METHOD_ADAGIO: - voices[voice].initial_volume = - gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, - voices[voice].expression_vol, - voices[voice].patch_vol); - break; - - case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ - voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); - break; - - default: - voices[voice].initial_volume = volume_base + - (voices[voice].midi_volume * volume_scale); - } - - if (voices[voice].initial_volume > 4030) - voices[voice].initial_volume = 4030; -} - -static void compute_and_set_volume(int voice, int volume, int ramp_time) -{ - int curr, target, rate; - unsigned long flags; - - compute_volume(voice, volume); - voices[voice].current_volume = voices[voice].initial_volume; - - spin_lock_irqsave(&gus_lock,flags); - /* - * CAUTION! Interrupts disabled. Enable them before returning - */ - - gus_select_voice(voice); - - curr = gus_read16(0x09) >> 4; - target = voices[voice].initial_volume; - - if (ramp_time == INSTANT_RAMP) - { - gus_rampoff(); - gus_voice_volume(target); - spin_unlock_irqrestore(&gus_lock,flags); - return; - } - if (ramp_time == FAST_RAMP) - rate = 63; - else - rate = 16; - gus_ramp_rate(0, rate); - - if ((target - curr) / 64 == 0) /* Close enough to target. */ - { - gus_rampoff(); - gus_voice_volume(target); - spin_unlock_irqrestore(&gus_lock,flags); - return; - } - if (target > curr) - { - if (target > (4095 - 65)) - target = 4095 - 65; - gus_ramp_range(curr, target); - gus_rampon(0x00); /* Ramp up, once, no IRQ */ - } - else - { - if (target < 65) - target = 65; - - gus_ramp_range(target, curr); - gus_rampon(0x40); /* Ramp down, once, no irq */ - } - spin_unlock_irqrestore(&gus_lock,flags); -} - -static void dynamic_volume_change(int voice) -{ - unsigned char status; - unsigned long flags; - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - status = gus_read8(0x00); /* Get voice status */ - spin_unlock_irqrestore(&gus_lock,flags); - - if (status & 0x03) - return; /* Voice was not running */ - - if (!(voices[voice].mode & WAVE_ENVELOPES)) - { - compute_and_set_volume(voice, voices[voice].midi_volume, 1); - return; - } - - /* - * Voice is running and has envelopes. - */ - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - status = gus_read8(0x0d); /* Ramping status */ - spin_unlock_irqrestore(&gus_lock,flags); - - if (status & 0x03) /* Sustain phase? */ - { - compute_and_set_volume(voice, voices[voice].midi_volume, 1); - return; - } - if (voices[voice].env_phase < 0) - return; - - compute_volume(voice, voices[voice].midi_volume); - -} - -static void guswave_controller(int dev, int voice, int ctrl_num, int value) -{ - unsigned long flags; - unsigned long freq; - - if (voice < 0 || voice > 31) - return; - - switch (ctrl_num) - { - case CTRL_PITCH_BENDER: - voices[voice].bender = value; - - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - { - freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0); - voices[voice].current_freq = freq; - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_freq(freq); - spin_unlock_irqrestore(&gus_lock,flags); - } - break; - - case CTRL_PITCH_BENDER_RANGE: - voices[voice].bender_range = value; - break; - case CTL_EXPRESSION: - value /= 128; - case CTRL_EXPRESSION: - if (volume_method == VOL_METHOD_ADAGIO) - { - voices[voice].expression_vol = value; - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - dynamic_volume_change(voice); - } - break; - - case CTL_PAN: - voices[voice].panning = (value * 2) - 128; - break; - - case CTL_MAIN_VOLUME: - value = (value * 100) / 16383; - - case CTRL_MAIN_VOLUME: - voices[voice].main_vol = value; - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - dynamic_volume_change(voice); - break; - - default: - break; - } -} - -static int guswave_start_note2(int dev, int voice, int note_num, int volume) -{ - int sample, best_sample, best_delta, delta_freq; - int is16bits, samplep, patch, pan; - unsigned long note_freq, base_note, freq, flags; - unsigned char mode = 0; - - if (voice < 0 || voice > 31) - { -/* printk("GUS: Invalid voice\n");*/ - return -EINVAL; - } - if (note_num == 255) - { - if (voices[voice].mode & WAVE_ENVELOPES) - { - voices[voice].midi_volume = volume; - dynamic_volume_change(voice); - return 0; - } - compute_and_set_volume(voice, volume, 1); - return 0; - } - if ((patch = patch_map[voice]) == -1) - return -EINVAL; - if ((samplep = patch_table[patch]) == NOT_SAMPLE) - { - return -EINVAL; - } - note_freq = note_to_freq(note_num); - - /* - * Find a sample within a patch so that the note_freq is between low_note - * and high_note. - */ - sample = -1; - - best_sample = samplep; - best_delta = 1000000; - while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) - { - delta_freq = note_freq - samples[samplep].base_note; - if (delta_freq < 0) - delta_freq = -delta_freq; - if (delta_freq < best_delta) - { - best_sample = samplep; - best_delta = delta_freq; - } - if (samples[samplep].low_note <= note_freq && - note_freq <= samples[samplep].high_note) - { - sample = samplep; - } - else - samplep = samples[samplep].key; /* Link to next sample */ - } - if (sample == -1) - sample = best_sample; - - if (sample == -1) - { -/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ - return 0; /* Should play default patch ??? */ - } - is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; - voices[voice].mode = samples[sample].mode; - voices[voice].patch_vol = samples[sample].volume; - - if (iw_mode) - gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ - - if (voices[voice].mode & WAVE_ENVELOPES) - { - int i; - - for (i = 0; i < 6; i++) - { - voices[voice].env_rate[i] = samples[sample].env_rate[i]; - voices[voice].env_offset[i] = samples[sample].env_offset[i]; - } - } - sample_map[voice] = sample; - - if (voices[voice].fixed_pitch) /* Fixed pitch */ - { - freq = samples[sample].base_freq; - } - else - { - base_note = samples[sample].base_note / 100; - note_freq /= 100; - - freq = samples[sample].base_freq * note_freq / base_note; - } - - voices[voice].orig_freq = freq; - - /* - * Since the pitch bender may have been set before playing the note, we - * have to calculate the bending now. - */ - - freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, - voices[voice].bender_range, 0); - voices[voice].current_freq = freq; - - pan = (samples[sample].panning + voices[voice].panning) / 32; - pan += 7; - if (pan < 0) - pan = 0; - if (pan > 15) - pan = 15; - - if (samples[sample].mode & WAVE_16_BITS) - { - mode |= 0x04; /* 16 bits */ - if ((sample_ptrs[sample] / GUS_BANK_SIZE) != - ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) - printk(KERN_ERR "GUS: Sample address error\n"); - } - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_off(); - gus_rampoff(); - - spin_unlock_irqrestore(&gus_lock,flags); - - if (voices[voice].mode & WAVE_ENVELOPES) - { - compute_volume(voice, volume); - init_envelope(voice); - } - else - { - compute_and_set_volume(voice, volume, 0); - } - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - - if (samples[sample].mode & WAVE_LOOP_BACK) - gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - - voices[voice].offset_pending, 0, is16bits); /* start=end */ - else - gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ - - if (samples[sample].mode & WAVE_LOOPING) - { - mode |= 0x08; - - if (samples[sample].mode & WAVE_BIDIR_LOOP) - mode |= 0x10; - - if (samples[sample].mode & WAVE_LOOP_BACK) - { - gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - - voices[voice].offset_pending, - (samples[sample].fractions >> 4) & 0x0f, is16bits); - mode |= 0x40; - } - gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, - samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ - gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, - (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ - } - else - { - mode |= 0x20; /* Loop IRQ at the end */ - voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ - voices[voice].loop_irq_parm = 1; - gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ - gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, - (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ - } - gus_voice_freq(freq); - gus_voice_balance(pan); - gus_voice_on(mode); - spin_unlock_irqrestore(&gus_lock,flags); - - return 0; -} - -/* - * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking - * when the note playing on the voice is changed. It uses volume - * ramping. - */ - -static int guswave_start_note(int dev, int voice, int note_num, int volume) -{ - unsigned long flags; - int mode; - int ret_val = 0; - - spin_lock_irqsave(&gus_lock,flags); - if (note_num == 255) - { - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].volume_pending = volume; - } - else - { - ret_val = guswave_start_note2(dev, voice, note_num, volume); - } - } - else - { - gus_select_voice(voice); - mode = gus_read8(0x00); - if (mode & 0x20) - gus_write8(0x00, mode & 0xdf); /* No interrupt! */ - - voices[voice].offset_pending = 0; - voices[voice].kill_pending = 0; - voices[voice].volume_irq_mode = 0; - voices[voice].loop_irq_mode = 0; - - if (voices[voice].sample_pending >= 0) - { - spin_unlock_irqrestore(&gus_lock,flags); /* Run temporarily with interrupts enabled */ - guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); - voices[voice].sample_pending = -1; - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); /* Reselect the voice (just to be sure) */ - } - if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) - { - ret_val = guswave_start_note2(dev, voice, note_num, volume); - } - else - { - voices[voice].dev_pending = dev; - voices[voice].note_pending = note_num; - voices[voice].volume_pending = volume; - voices[voice].volume_irq_mode = VMODE_START_NOTE; - - gus_rampoff(); - gus_ramp_range(2000, 4065); - gus_ramp_rate(0, 63); /* Fastest possible rate */ - gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ - } - } - spin_unlock_irqrestore(&gus_lock,flags); - return ret_val; -} - -static void guswave_reset(int dev) -{ - int i; - - for (i = 0; i < 32; i++) - { - gus_voice_init(i); - gus_voice_init2(i); - } -} - -static int guswave_open(int dev, int mode) -{ - int err; - - if (gus_busy) - return -EBUSY; - - voice_alloc->timestamp = 0; - - if (gus_no_wave_dma) { - gus_no_dma = 1; - } else { - if ((err = DMAbuf_open_dma(gus_devnum)) < 0) - { - /* printk( "GUS: Loading samples without DMA\n"); */ - gus_no_dma = 1; /* Upload samples using PIO */ - } - else - gus_no_dma = 0; - } - - init_waitqueue_head(&dram_sleeper); - gus_busy = 1; - active_device = GUS_DEV_WAVE; - - gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - gus_initialize(); - gus_reset(); - gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - - return 0; -} - -static void guswave_close(int dev) -{ - gus_busy = 0; - active_device = 0; - gus_reset(); - - if (!gus_no_dma) - DMAbuf_close_dma(gus_devnum); -} - -static int guswave_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) -{ - struct patch_info patch; - int instr; - long sizeof_patch; - - unsigned long blk_sz, blk_end, left, src_offs, target; - - sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ - - if (format != GUS_PATCH) - { -/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ - return -EINVAL; - } - if (count < sizeof_patch) - { -/* printk("GUS Error: Patch header too short\n");*/ - return -EINVAL; - } - count -= sizeof_patch; - - if (free_sample >= MAX_SAMPLE) - { -/* printk("GUS: Sample table full\n");*/ - return -ENOSPC; - } - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ - - if (copy_from_user(&((char *) &patch)[offs], &(addr)[offs], - sizeof_patch - offs)) - return -EFAULT; - - if (patch.mode & WAVE_ROM) - return -EINVAL; - if (gus_mem_size == 0) - return -ENOSPC; - - instr = patch.instr_no; - - if (instr < 0 || instr > MAX_PATCH) - { -/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/ - return -EINVAL; - } - if (count < patch.len) - { -/* printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/ - patch.len = count; - } - if (patch.len <= 0 || patch.len > gus_mem_size) - { -/* printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/ - return -EINVAL; - } - if (patch.mode & WAVE_LOOPING) - { - if (patch.loop_start < 0 || patch.loop_start >= patch.len) - { -/* printk(KERN_ERR "GUS: Invalid loop start\n");*/ - return -EINVAL; - } - if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) - { -/* printk(KERN_ERR "GUS: Invalid loop end\n");*/ - return -EINVAL; - } - } - free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ - - if (patch.mode & WAVE_16_BITS) - { - /* - * 16 bit samples must fit one 256k bank. - */ - if (patch.len >= GUS_BANK_SIZE) - { -/* printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/ - return -ENOSPC; - } - if ((free_mem_ptr / GUS_BANK_SIZE) != - ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) - { - unsigned long tmp_mem = - /* Align to 256K */ - ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; - - if ((tmp_mem + patch.len) > gus_mem_size) - return -ENOSPC; - - free_mem_ptr = tmp_mem; /* This leaves unusable memory */ - } - } - if ((free_mem_ptr + patch.len) > gus_mem_size) - return -ENOSPC; - - sample_ptrs[free_sample] = free_mem_ptr; - - /* - * Tremolo is not possible with envelopes - */ - - if (patch.mode & WAVE_ENVELOPES) - patch.mode &= ~WAVE_TREMOLO; - - if (!(patch.mode & WAVE_FRACTIONS)) - { - patch.fractions = 0; - } - memcpy((char *) &samples[free_sample], &patch, sizeof_patch); - - /* - * Link this_one sample to the list of samples for patch 'instr'. - */ - - samples[free_sample].key = patch_table[instr]; - patch_table[instr] = free_sample; - - /* - * Use DMA to transfer the wave data to the DRAM - */ - - left = patch.len; - src_offs = 0; - target = free_mem_ptr; - - while (left) /* Not completely transferred yet */ - { - blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; - if (blk_sz > left) - blk_sz = left; - - /* - * DMA cannot cross bank (256k) boundaries. Check for that. - */ - - blk_end = target + blk_sz; - - if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE)) - { - /* Split the block */ - blk_end &= ~(GUS_BANK_SIZE - 1); - blk_sz = blk_end - target; - } - if (gus_no_dma) - { - /* - * For some reason the DMA is not possible. We have to use PIO. - */ - long i; - unsigned char data; - - for (i = 0; i < blk_sz; i++) - { - get_user(*(unsigned char *) &data, (unsigned char __user *) &((addr)[sizeof_patch + i])); - if (patch.mode & WAVE_UNSIGNED) - if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) - data ^= 0x80; /* Convert to signed */ - gus_poke(target + i, data); - } - } - else - { - unsigned long address, hold_address; - unsigned char dma_command; - unsigned long flags; - - if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) - { - printk(KERN_ERR "GUS: DMA buffer == NULL\n"); - return -ENOSPC; - } - /* - * OK, move now. First in and then out. - */ - - if (copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, - &(addr)[sizeof_patch + src_offs], - blk_sz)) - return -EFAULT; - - spin_lock_irqsave(&gus_lock,flags); - gus_write8(0x41, 0); /* Disable GF1 DMA */ - DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys, - blk_sz, DMA_MODE_WRITE); - - /* - * Set the DRAM address for the wave data - */ - - if (iw_mode) - { - /* Different address translation in enhanced mode */ - - unsigned char hi; - - if (gus_dma > 4) - address = target >> 1; /* Convert to 16 bit word address */ - else - address = target; - - hi = (unsigned char) ((address >> 16) & 0xf0); - hi += (unsigned char) (address & 0x0f); - - gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */ - gus_write8(0x50, hi); - } - else - { - address = target; - if (audio_devs[gus_devnum]->dmap_out->dma > 3) - { - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - } - - /* - * Start the DMA transfer - */ - - dma_command = 0x21; /* IRQ enable, DMA start */ - if (patch.mode & WAVE_UNSIGNED) - dma_command |= 0x80; /* Invert MSB */ - if (patch.mode & WAVE_16_BITS) - dma_command |= 0x40; /* 16 bit _DATA_ */ - if (audio_devs[gus_devnum]->dmap_out->dma > 3) - dma_command |= 0x04; /* 16 bit DMA _channel_ */ - - /* - * Sleep here until the DRAM DMA done interrupt is served - */ - active_device = GUS_DEV_WAVE; - gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */ - - spin_unlock_irqrestore(&gus_lock,flags); /* opens a race */ - if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ)) - printk("GUS: DMA Transfer timed out\n"); - } - - /* - * Now the next part - */ - - left -= blk_sz; - src_offs += blk_sz; - target += blk_sz; - - gus_write8(0x41, 0); /* Stop DMA */ - } - - free_mem_ptr += patch.len; - free_sample++; - return 0; -} - -static void guswave_hw_control(int dev, unsigned char *event_rec) -{ - int voice, cmd; - unsigned short p1, p2; - unsigned int plong; - unsigned long flags; - - cmd = event_rec[2]; - voice = event_rec[3]; - p1 = *(unsigned short *) &event_rec[4]; - p2 = *(unsigned short *) &event_rec[6]; - plong = *(unsigned int *) &event_rec[4]; - - if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && - (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) - do_volume_irq(voice); - - switch (cmd) - { - case _GUS_NUMVOICES: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_select_max_voices(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICESAMPLE: - guswave_set_instr(dev, voice, p1); - break; - - case _GUS_VOICEON: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_voice_on(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICEOFF: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_off(); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICEFADE: - gus_voice_fade(voice); - break; - - case _GUS_VOICEMODE: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_voice_mode(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICEBALA: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_balance(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICEFREQ: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_freq(plong); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICEVOL: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_volume(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOICEVOL2: /* Just update the software voice level */ - voices[voice].initial_volume = voices[voice].current_volume = p1; - break; - - case _GUS_RAMPRANGE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_ramp_range(p1, p2); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_RAMPRATE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NJET-NJET */ - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_ramp_rate(p1, p2); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_RAMPMODE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_ramp_mode(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_RAMPON: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* EI-EI */ - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_rampon(p1); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_RAMPOFF: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NEJ-NEJ */ - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_rampoff(); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - case _GUS_VOLUME_SCALE: - volume_base = p1; - volume_scale = p2; - break; - - case _GUS_VOICE_POS: - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_set_voice_pos(voice, plong); - spin_unlock_irqrestore(&gus_lock,flags); - break; - - default: - break; - } -} - -static int gus_audio_set_speed(int speed) -{ - if (speed <= 0) - speed = gus_audio_speed; - - if (speed < 4000) - speed = 4000; - - if (speed > 44100) - speed = 44100; - - gus_audio_speed = speed; - - if (only_read_access) - { - /* Compute nearest valid recording speed and return it */ - - /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ - speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; - speed = (9878400 / (speed * 16)) - 2; - } - return speed; -} - -static int gus_audio_set_channels(int channels) -{ - if (!channels) - return gus_audio_channels; - if (channels > 2) - channels = 2; - if (channels < 1) - channels = 1; - gus_audio_channels = channels; - return channels; -} - -static int gus_audio_set_bits(int bits) -{ - if (!bits) - return gus_audio_bits; - - if (bits != 8 && bits != 16) - bits = 8; - - if (only_8_bits) - bits = 8; - - gus_audio_bits = bits; - return bits; -} - -static int gus_audio_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int val; - - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (get_user(val, (int __user*)arg)) - return -EFAULT; - val = gus_audio_set_speed(val); - break; - - case SOUND_PCM_READ_RATE: - val = gus_audio_speed; - break; - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = gus_audio_set_channels(val + 1) - 1; - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = gus_audio_set_channels(val); - break; - - case SOUND_PCM_READ_CHANNELS: - val = gus_audio_channels; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = gus_audio_set_bits(val); - break; - - case SOUND_PCM_READ_BITS: - val = gus_audio_bits; - break; - - case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ - case SOUND_PCM_READ_FILTER: - val = -EINVAL; - break; - default: - return -EINVAL; - } - return put_user(val, (int __user *)arg); -} - -static void gus_audio_reset(int dev) -{ - if (recording_active) - { - gus_write8(0x49, 0x00); /* Halt recording */ - set_input_volumes(); - } -} - -static int saved_iw_mode; /* A hack hack hack */ - -static int gus_audio_open(int dev, int mode) -{ - if (gus_busy) - return -EBUSY; - - if (gus_pnp_flag && mode & OPEN_READ) - { -/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/ - return -EIO; - } - gus_initialize(); - - gus_busy = 1; - active_device = 0; - - saved_iw_mode = iw_mode; - if (iw_mode) - { - /* There are some problems with audio in enhanced mode so disable it */ - gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */ - iw_mode = 0; - } - - gus_reset(); - reset_sample_memory(); - gus_select_max_voices(14); - - pcm_active = 0; - dma_active = 0; - pcm_opened = 1; - if (mode & OPEN_READ) - { - recording_active = 1; - set_input_volumes(); - } - only_read_access = !(mode & OPEN_WRITE); - only_8_bits = mode & OPEN_READ; - if (only_8_bits) - audio_devs[dev]->format_mask = AFMT_U8; - else - audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; - - return 0; -} - -static void gus_audio_close(int dev) -{ - iw_mode = saved_iw_mode; - gus_reset(); - gus_busy = 0; - pcm_opened = 0; - active_device = 0; - - if (recording_active) - { - gus_write8(0x49, 0x00); /* Halt recording */ - set_input_volumes(); - } - recording_active = 0; -} - -static void gus_audio_update_volume(void) -{ - unsigned long flags; - int voice; - - if (pcm_active && pcm_opened) - for (voice = 0; voice < gus_audio_channels; voice++) - { - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_rampoff(); - gus_voice_volume(1530 + (25 * gus_pcm_volume)); - gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); - spin_unlock_irqrestore(&gus_lock,flags); - } -} - -static void play_next_pcm_block(void) -{ - unsigned long flags; - int speed = gus_audio_speed; - int this_one, is16bits, chn; - unsigned long dram_loc; - unsigned char mode[2], ramp_mode[2]; - - if (!pcm_qlen) - return; - - this_one = pcm_head; - - for (chn = 0; chn < gus_audio_channels; chn++) - { - mode[chn] = 0x00; - ramp_mode[chn] = 0x03; /* Ramping and rollover off */ - - if (chn == 0) - { - mode[chn] |= 0x20; /* Loop IRQ */ - voices[chn].loop_irq_mode = LMODE_PCM; - } - if (gus_audio_bits != 8) - { - is16bits = 1; - mode[chn] |= 0x04; /* 16 bit data */ - } - else - is16bits = 0; - - dram_loc = this_one * pcm_bsize; - dram_loc += chn * pcm_banksize; - - if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ - { - mode[chn] |= 0x08; /* Enable loop */ - ramp_mode[chn] = 0x03; /* Disable rollover bit */ - } - else - { - if (chn == 0) - ramp_mode[chn] = 0x04; /* Enable rollover bit */ - } - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(chn); - gus_voice_freq(speed); - - if (gus_audio_channels == 1) - gus_voice_balance(7); /* mono */ - else if (chn == 0) - gus_voice_balance(0); /* left */ - else - gus_voice_balance(15); /* right */ - - if (!pcm_active) /* Playback not already active */ - { - /* - * The playback was not started yet (or there has been a pause). - * Start the voice (again) and ask for a rollover irq at the end of - * this_one block. If this_one one is last of the buffers, use just - * the normal loop with irq. - */ - - gus_voice_off(); - gus_rampoff(); - gus_voice_volume(1530 + (25 * gus_pcm_volume)); - gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); - - gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ - gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ - - if (chn != 0) - gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, - 0, is16bits); /* Loop end location */ - } - if (chn == 0) - gus_write_addr(0x04, dram_loc + pcm_bsize - 1, - 0, is16bits); /* Loop end location */ - else - mode[chn] |= 0x08; /* Enable looping */ - spin_unlock_irqrestore(&gus_lock,flags); - } - for (chn = 0; chn < gus_audio_channels; chn++) - { - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(chn); - gus_write8(0x0d, ramp_mode[chn]); - if (iw_mode) - gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ - gus_voice_on(mode[chn]); - spin_unlock_irqrestore(&gus_lock,flags); - } - pcm_active = 1; -} - -static void gus_transfer_output_block(int dev, unsigned long buf, - int total_count, int intrflag, int chn) -{ - /* - * This routine transfers one block of audio data to the DRAM. In mono mode - * it's called just once. When in stereo mode, this_one routine is called - * once for both channels. - * - * The left/mono channel data is transferred to the beginning of dram and the - * right data to the area pointed by gus_page_size. - */ - - int this_one, count; - unsigned long flags; - unsigned char dma_command; - unsigned long address, hold_address; - - spin_lock_irqsave(&gus_lock,flags); - - count = total_count / gus_audio_channels; - - if (chn == 0) - { - if (pcm_qlen >= pcm_nblk) - printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n"); - - this_one = pcm_current_block = pcm_tail; - pcm_qlen++; - pcm_tail = (pcm_tail + 1) % pcm_nblk; - pcm_datasize[this_one] = count; - } - else - this_one = pcm_current_block; - - gus_write8(0x41, 0); /* Disable GF1 DMA */ - DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE); - - address = this_one * pcm_bsize; - address += chn * pcm_banksize; - - if (audio_devs[dev]->dmap_out->dma > 3) - { - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - - dma_command = 0x21; /* IRQ enable, DMA start */ - - if (gus_audio_bits != 8) - dma_command |= 0x40; /* 16 bit _DATA_ */ - else - dma_command |= 0x80; /* Invert MSB */ - - if (audio_devs[dev]->dmap_out->dma > 3) - dma_command |= 0x04; /* 16 bit DMA channel */ - - gus_write8(0x41, dma_command); /* Kick start */ - - if (chn == (gus_audio_channels - 1)) /* Last channel */ - { - /* - * Last (right or mono) channel data - */ - dma_active = 1; /* DMA started. There is a unacknowledged buffer */ - active_device = GUS_DEV_PCM_DONE; - if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize)) - { - play_next_pcm_block(); - } - } - else - { - /* - * Left channel data. The right channel - * is transferred after DMA interrupt - */ - active_device = GUS_DEV_PCM_CONTINUE; - } - - spin_unlock_irqrestore(&gus_lock,flags); -} - -static void gus_uninterleave8(char *buf, int l) -{ -/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ - int i, p = 0, halfsize = l / 2; - char *buf2 = buf + halfsize, *src = bounce_buf; - - memcpy(bounce_buf, buf, l); - - for (i = 0; i < halfsize; i++) - { - buf[i] = src[p++]; /* Left channel */ - buf2[i] = src[p++]; /* Right channel */ - } -} - -static void gus_uninterleave16(short *buf, int l) -{ -/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ - int i, p = 0, halfsize = l / 2; - short *buf2 = buf + halfsize, *src = (short *) bounce_buf; - - memcpy(bounce_buf, (char *) buf, l * 2); - - for (i = 0; i < halfsize; i++) - { - buf[i] = src[p++]; /* Left channel */ - buf2[i] = src[p++]; /* Right channel */ - } -} - -static void gus_audio_output_block(int dev, unsigned long buf, int total_count, - int intrflag) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; - - pcm_current_buf = buf; - pcm_current_count = total_count; - pcm_current_intrflag = intrflag; - pcm_current_dev = dev; - if (gus_audio_channels == 2) - { - char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); - - if (gus_audio_bits == 8) - gus_uninterleave8(b, total_count); - else - gus_uninterleave16((short *) b, total_count / 2); - } - gus_transfer_output_block(dev, buf, total_count, intrflag, 0); -} - -static void gus_audio_start_input(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags; - unsigned char mode; - - spin_lock_irqsave(&gus_lock,flags); - - DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); - mode = 0xa0; /* DMA IRQ enabled, invert MSB */ - - if (audio_devs[dev]->dmap_in->dma > 3) - mode |= 0x04; /* 16 bit DMA channel */ - if (gus_audio_channels > 1) - mode |= 0x02; /* Stereo */ - mode |= 0x01; /* DMA enable */ - - gus_write8(0x49, mode); - spin_unlock_irqrestore(&gus_lock,flags); -} - -static int gus_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - unsigned int rate; - - gus_audio_bsize = bsize; - audio_devs[dev]->dmap_in->flags |= DMA_NODMA; - rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; - - gus_write8(0x48, rate & 0xff); /* Set sampling rate */ - - if (gus_audio_bits != 8) - { -/* printk("GUS Error: 16 bit recording not supported\n");*/ - return -EINVAL; - } - return 0; -} - -static int gus_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - int i; - - long mem_ptr, mem_size; - - audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; - mem_ptr = 0; - mem_size = gus_mem_size / gus_audio_channels; - - if (mem_size > (256 * 1024)) - mem_size = 256 * 1024; - - pcm_bsize = bsize / gus_audio_channels; - pcm_head = pcm_tail = pcm_qlen = 0; - - pcm_nblk = 2; /* MAX_PCM_BUFFERS; */ - if ((pcm_bsize * pcm_nblk) > mem_size) - pcm_nblk = mem_size / pcm_bsize; - - for (i = 0; i < pcm_nblk; i++) - pcm_datasize[i] = 0; - - pcm_banksize = pcm_nblk * pcm_bsize; - - if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) - pcm_nblk--; - gus_write8(0x41, 0); /* Disable GF1 DMA */ - return 0; -} - -static int gus_local_qlen(int dev) -{ - return pcm_qlen; -} - - -static struct audio_driver gus_audio_driver = -{ - .owner = THIS_MODULE, - .open = gus_audio_open, - .close = gus_audio_close, - .output_block = gus_audio_output_block, - .start_input = gus_audio_start_input, - .ioctl = gus_audio_ioctl, - .prepare_for_input = gus_audio_prepare_for_input, - .prepare_for_output = gus_audio_prepare_for_output, - .halt_io = gus_audio_reset, - .local_qlen = gus_local_qlen, -}; - -static void guswave_setup_voice(int dev, int voice, int chn) -{ - struct channel_info *info = &synth_devs[dev]->chn_info[chn]; - - guswave_set_instr(dev, voice, info->pgm_num); - voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ - voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; - voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; - voices[voice].bender = 0; - voices[voice].bender_range = info->bender_range; - - if (chn == 9) - voices[voice].fixed_pitch = 1; -} - -static void guswave_bender(int dev, int voice, int value) -{ - int freq; - unsigned long flags; - - voices[voice].bender = value - 8192; - freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0); - voices[voice].current_freq = freq; - - spin_lock_irqsave(&gus_lock,flags); - gus_select_voice(voice); - gus_voice_freq(freq); - spin_unlock_irqrestore(&gus_lock,flags); -} - -static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) -{ - int i, p, best = -1, best_time = 0x7fffffff; - - p = alloc->ptr; - /* - * First look for a completely stopped voice - */ - - for (i = 0; i < alloc->max_voice; i++) - { - if (alloc->map[p] == 0) - { - alloc->ptr = p; - return p; - } - if (alloc->alloc_times[p] < best_time) - { - best = p; - best_time = alloc->alloc_times[p]; - } - p = (p + 1) % alloc->max_voice; - } - - /* - * Then look for a releasing voice - */ - - for (i = 0; i < alloc->max_voice; i++) - { - if (alloc->map[p] == 0xffff) - { - alloc->ptr = p; - return p; - } - p = (p + 1) % alloc->max_voice; - } - if (best >= 0) - p = best; - - alloc->ptr = p; - return p; -} - -static struct synth_operations guswave_operations = -{ - .owner = THIS_MODULE, - .id = "GUS", - .info = &gus_info, - .midi_dev = 0, - .synth_type = SYNTH_TYPE_SAMPLE, - .synth_subtype = SAMPLE_TYPE_GUS, - .open = guswave_open, - .close = guswave_close, - .ioctl = guswave_ioctl, - .kill_note = guswave_kill_note, - .start_note = guswave_start_note, - .set_instr = guswave_set_instr, - .reset = guswave_reset, - .hw_control = guswave_hw_control, - .load_patch = guswave_load_patch, - .aftertouch = guswave_aftertouch, - .controller = guswave_controller, - .panning = guswave_panning, - .volume_method = guswave_volume_method, - .bender = guswave_bender, - .alloc_voice = guswave_alloc, - .setup_voice = guswave_setup_voice -}; - -static void set_input_volumes(void) -{ - unsigned long flags; - unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ - - if (have_gus_max) /* Don't disturb GUS MAX */ - return; - - spin_lock_irqsave(&gus_lock,flags); - - /* - * Enable channels having vol > 10% - * Note! bit 0x01 means the line in DISABLED while 0x04 means - * the mic in ENABLED. - */ - if (gus_line_vol > 10) - mask &= ~0x01; - if (gus_mic_vol > 10) - mask |= 0x04; - - if (recording_active) - { - /* - * Disable channel, if not selected for recording - */ - if (!(gus_recmask & SOUND_MASK_LINE)) - mask |= 0x01; - if (!(gus_recmask & SOUND_MASK_MIC)) - mask &= ~0x04; - } - mix_image &= ~0x07; - mix_image |= mask & 0x07; - outb((mix_image), u_Mixer); - - spin_unlock_irqrestore(&gus_lock,flags); -} - -#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ - SOUND_MASK_SYNTH|SOUND_MASK_PCM) - -int gus_default_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int vol, val; - - if (((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if (!access_ok(VERIFY_WRITE, arg, sizeof(int))) - return -EFAULT; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - { - if (__get_user(val, (int __user *) arg)) - return -EFAULT; - - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - gus_recmask = val & MIX_DEVS; - if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) - gus_recmask = SOUND_MASK_MIC; - /* Note! Input volumes are updated during next open for recording */ - val = gus_recmask; - break; - - case SOUND_MIXER_MIC: - vol = val & 0xff; - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - gus_mic_vol = vol; - set_input_volumes(); - val = vol | (vol << 8); - break; - - case SOUND_MIXER_LINE: - vol = val & 0xff; - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - gus_line_vol = vol; - set_input_volumes(); - val = vol | (vol << 8); - break; - - case SOUND_MIXER_PCM: - gus_pcm_volume = val & 0xff; - if (gus_pcm_volume < 0) - gus_pcm_volume = 0; - if (gus_pcm_volume > 100) - gus_pcm_volume = 100; - gus_audio_update_volume(); - val = gus_pcm_volume | (gus_pcm_volume << 8); - break; - - case SOUND_MIXER_SYNTH: - gus_wave_volume = val & 0xff; - if (gus_wave_volume < 0) - gus_wave_volume = 0; - if (gus_wave_volume > 100) - gus_wave_volume = 100; - if (active_device == GUS_DEV_WAVE) - { - int voice; - for (voice = 0; voice < nr_voices; voice++) - dynamic_volume_change(voice); /* Apply the new vol */ - } - val = gus_wave_volume | (gus_wave_volume << 8); - break; - - default: - return -EINVAL; - } - } - else - { - switch (cmd & 0xff) - { - /* - * Return parameters - */ - case SOUND_MIXER_RECSRC: - val = gus_recmask; - break; - - case SOUND_MIXER_DEVMASK: - val = MIX_DEVS; - break; - - case SOUND_MIXER_STEREODEVS: - val = 0; - break; - - case SOUND_MIXER_RECMASK: - val = SOUND_MASK_MIC | SOUND_MASK_LINE; - break; - - case SOUND_MIXER_CAPS: - val = 0; - break; - - case SOUND_MIXER_MIC: - val = gus_mic_vol | (gus_mic_vol << 8); - break; - - case SOUND_MIXER_LINE: - val = gus_line_vol | (gus_line_vol << 8); - break; - - case SOUND_MIXER_PCM: - val = gus_pcm_volume | (gus_pcm_volume << 8); - break; - - case SOUND_MIXER_SYNTH: - val = gus_wave_volume | (gus_wave_volume << 8); - break; - - default: - return -EINVAL; - } - } - return __put_user(val, (int __user *)arg); -} - -static struct mixer_operations gus_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "GUS", - .name = "Gravis Ultrasound", - .ioctl = gus_default_mixer_ioctl -}; - -static int __init gus_default_mixer_init(void) -{ - int n; - - if ((n = sound_alloc_mixerdev()) != -1) - { - /* - * Don't install if there is another - * mixer - */ - mixer_devs[n] = &gus_mixer_operations; - } - if (have_gus_max) - { - /* - * Enable all mixer channels on the GF1 side. Otherwise recording will - * not be possible using GUS MAX. - */ - mix_image &= ~0x07; - mix_image |= 0x04; /* All channels enabled */ - outb((mix_image), u_Mixer); - } - return n; -} - -void __init gus_wave_init(struct address_info *hw_config) -{ - unsigned long flags; - unsigned char val; - char *model_num = "2.4"; - char tmp[64]; - int gus_type = 0x24; /* 2.4 */ - - int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; - int sdev; - - hw_config->slots[0] = -1; /* No wave */ - hw_config->slots[1] = -1; /* No ad1848 */ - hw_config->slots[4] = -1; /* No audio */ - hw_config->slots[5] = -1; /* No mixer */ - - if (!gus_pnp_flag) - { - if (irq < 0 || irq > 15) - { - printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq); - return; - } - } - - if (dma < 0 || dma > 7 || dma == 4) - { - printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma); - return; - } - gus_irq = irq; - gus_dma = dma; - gus_dma2 = dma2; - gus_hw_config = hw_config; - - if (gus_dma2 == -1) - gus_dma2 = dma; - - /* - * Try to identify the GUS model. - * - * Versions < 3.6 don't have the digital ASIC. Try to probe it first. - */ - - spin_lock_irqsave(&gus_lock,flags); - outb((0x20), gus_base + 0x0f); - val = inb(gus_base + 0x0f); - spin_unlock_irqrestore(&gus_lock,flags); - - if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ - { - int ad_flags = 0; - - if (gus_pnp_flag) - ad_flags = 0x12345678; /* Interwave "magic" */ - /* - * It has the digital ASIC so the card is at least v3.4. - * Next try to detect the true model. - */ - - if (gus_pnp_flag) /* Hack hack hack */ - val = 10; - else - val = inb(u_MixSelect); - - /* - * Value 255 means pre-3.7 which don't have mixer. - * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. - * 10 and above is GUS MAX which has the CS4231 codec/mixer. - * - */ - - if (val == 255 || val < 5) - { - model_num = "3.4"; - gus_type = 0x34; - } - else if (val < 10) - { - model_num = "3.7"; - gus_type = 0x37; - mixer_type = ICS2101; - request_region(u_MixSelect, 1, "GUS mixer"); - } - else - { - struct resource *ports; - ports = request_region(gus_base + 0x10c, 4, "ad1848"); - model_num = "MAX"; - gus_type = 0x40; - mixer_type = CS4231; -#ifdef CONFIG_SOUND_GUSMAX - { - unsigned char max_config = 0x40; /* Codec enable */ - - if (gus_dma2 == -1) - gus_dma2 = gus_dma; - - if (gus_dma > 3) - max_config |= 0x10; /* 16 bit capture DMA */ - - if (gus_dma2 > 3) - max_config |= 0x20; /* 16 bit playback DMA */ - - max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ - - outb((max_config), gus_base + 0x106); /* UltraMax control */ - } - - if (!ports) - goto no_cs4231; - - if (ad1848_detect(ports, &ad_flags, hw_config->osp)) - { - char *name = "GUS MAX"; - int old_num_mixers = num_mixers; - - if (gus_pnp_flag) - name = "GUS PnP"; - - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - have_gus_max = 1; - if (hw_config->name) - name = hw_config->name; - - hw_config->slots[1] = ad1848_init(name, ports, - -irq, gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1, /* Share DMA channels with GF1 */ - hw_config->osp, - THIS_MODULE); - - if (num_mixers > old_num_mixers) - { - /* GUS has it's own mixer map */ - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); - } - } - else { - release_region(gus_base + 0x10c, 4); - no_cs4231: - printk(KERN_WARNING "GUS: No CS4231 ??"); - } -#else - printk(KERN_ERR "GUS MAX found, but not compiled in\n"); -#endif - } - } - else - { - /* - * ASIC not detected so the card must be 2.2 or 2.4. - * There could still be the 16-bit/mixer daughter card. - */ - } - - if (hw_config->name) - snprintf(tmp, sizeof(tmp), "%s (%dk)", hw_config->name, - (int) gus_mem_size / 1024); - else if (gus_pnp_flag) - snprintf(tmp, sizeof(tmp), "Gravis UltraSound PnP (%dk)", - (int) gus_mem_size / 1024); - else - snprintf(tmp, sizeof(tmp), "Gravis UltraSound %s (%dk)", model_num, - (int) gus_mem_size / 1024); - - - samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); - if (samples == NULL) - { - printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); - return; - } - conf_printf(tmp, hw_config); - strlcpy(gus_info.name, tmp, sizeof(gus_info.name)); - - if ((sdev = sound_alloc_synthdev()) == -1) - printk(KERN_WARNING "gus_init: Too many synthesizers\n"); - else - { - voice_alloc = &guswave_operations.alloc; - if (iw_mode) - guswave_operations.id = "IWAVE"; - hw_config->slots[0] = sdev; - synth_devs[sdev] = &guswave_operations; - sequencer_init(); - gus_tmr_install(gus_base + 8); - } - - reset_sample_memory(); - - gus_initialize(); - - if ((gus_mem_size > 0) && !gus_no_wave_dma) - { - hw_config->slots[4] = -1; - if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - "Ultrasound", - &gus_audio_driver, - sizeof(struct audio_driver), - NEEDS_RESTART | - ((!iw_mode && dma2 != dma && dma2 != -1) ? - DMA_DUPLEX : 0), - AFMT_U8 | AFMT_S16_LE, - NULL, dma, dma2)) < 0) - { - return; - } - - hw_config->slots[4] = gus_devnum; - audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ - audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ - audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ - audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; - } - - /* - * Mixer dependent initialization. - */ - - switch (mixer_type) - { - case ICS2101: - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - request_region(u_MixSelect, 1, "GUS mixer"); - hw_config->slots[5] = ics2101_mixer_init(); - audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ - return; - - case CS4231: - /* Initialized elsewhere (ad1848.c) */ - default: - hw_config->slots[5] = gus_default_mixer_init(); - audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ - return; - } -} - -void __exit gus_wave_unload(struct address_info *hw_config) -{ -#ifdef CONFIG_SOUND_GUSMAX - if (have_gus_max) - { - ad1848_unload(gus_base + 0x10c, - -gus_irq, - gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1); /* Share DMA channels with GF1 */ - } -#endif - - if (mixer_type == ICS2101) - { - release_region(u_MixSelect, 1); - } - if (hw_config->slots[0] != -1) - sound_unload_synthdev(hw_config->slots[0]); - if (hw_config->slots[1] != -1) - sound_unload_audiodev(hw_config->slots[1]); - if (hw_config->slots[2] != -1) - sound_unload_mididev(hw_config->slots[2]); - if (hw_config->slots[4] != -1) - sound_unload_audiodev(hw_config->slots[4]); - if (hw_config->slots[5] != -1) - sound_unload_mixerdev(hw_config->slots[5]); - - vfree(samples); - samples=NULL; -} -/* called in interrupt context */ -static void do_loop_irq(int voice) -{ - unsigned char tmp; - int mode, parm; - - spin_lock(&gus_lock); - gus_select_voice(voice); - - tmp = gus_read8(0x00); - tmp &= ~0x20; /* - * Disable wave IRQ for this_one voice - */ - gus_write8(0x00, tmp); - - if (tmp & 0x03) /* Voice stopped */ - voice_alloc->map[voice] = 0; - - mode = voices[voice].loop_irq_mode; - voices[voice].loop_irq_mode = 0; - parm = voices[voice].loop_irq_parm; - - switch (mode) - { - case LMODE_FINISH: /* - * Final loop finished, shoot volume down - */ - - if ((int) (gus_read16(0x09) >> 4) < 100) /* - * Get current volume - */ - { - gus_voice_off(); - gus_rampoff(); - gus_voice_init(voice); - break; - } - gus_ramp_range(65, 4065); - gus_ramp_rate(0, 63); /* - * Fastest possible rate - */ - gus_rampon(0x20 | 0x40); /* - * Ramp down, once, irq - */ - voices[voice].volume_irq_mode = VMODE_HALT; - break; - - case LMODE_PCM_STOP: - pcm_active = 0; /* Signal to the play_next_pcm_block routine */ - case LMODE_PCM: - { - pcm_qlen--; - pcm_head = (pcm_head + 1) % pcm_nblk; - if (pcm_qlen && pcm_active) - { - play_next_pcm_block(); - } - else - { - /* Underrun. Just stop the voice */ - gus_select_voice(0); /* Left channel */ - gus_voice_off(); - gus_rampoff(); - gus_select_voice(1); /* Right channel */ - gus_voice_off(); - gus_rampoff(); - pcm_active = 0; - } - - /* - * If the queue was full before this interrupt, the DMA transfer was - * suspended. Let it continue now. - */ - - if (audio_devs[gus_devnum]->dmap_out->qlen > 0) - DMAbuf_outputintr(gus_devnum, 0); - } - break; - - default: - break; - } - spin_unlock(&gus_lock); -} - -static void do_volume_irq(int voice) -{ - unsigned char tmp; - int mode, parm; - unsigned long flags; - - spin_lock_irqsave(&gus_lock,flags); - - gus_select_voice(voice); - tmp = gus_read8(0x0d); - tmp &= ~0x20; /* - * Disable volume ramp IRQ - */ - gus_write8(0x0d, tmp); - - mode = voices[voice].volume_irq_mode; - voices[voice].volume_irq_mode = 0; - parm = voices[voice].volume_irq_parm; - - switch (mode) - { - case VMODE_HALT: /* Decay phase finished */ - if (iw_mode) - gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */ - spin_unlock_irqrestore(&gus_lock,flags); - gus_voice_init(voice); - break; - - case VMODE_ENVELOPE: - gus_rampoff(); - spin_unlock_irqrestore(&gus_lock,flags); - step_envelope(voice); - break; - - case VMODE_START_NOTE: - spin_unlock_irqrestore(&gus_lock,flags); - guswave_start_note2(voices[voice].dev_pending, voice, - voices[voice].note_pending, voices[voice].volume_pending); - if (voices[voice].kill_pending) - guswave_kill_note(voices[voice].dev_pending, voice, - voices[voice].note_pending, 0); - - if (voices[voice].sample_pending >= 0) - { - guswave_set_instr(voices[voice].dev_pending, voice, - voices[voice].sample_pending); - voices[voice].sample_pending = -1; - } - break; - - default: - spin_unlock_irqrestore(&gus_lock,flags); - } -} -/* called in irq context */ -void gus_voice_irq(void) -{ - unsigned long wave_ignore = 0, volume_ignore = 0; - unsigned long voice_bit; - - unsigned char src, voice; - - while (1) - { - src = gus_read8(0x0f); /* - * Get source info - */ - voice = src & 0x1f; - src &= 0xc0; - - if (src == (0x80 | 0x40)) - return; /* - * No interrupt - */ - - voice_bit = 1 << voice; - - if (!(src & 0x80)) /* - * Wave IRQ pending - */ - if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* - * Not done - * yet - */ - { - wave_ignore |= voice_bit; - do_loop_irq(voice); - } - if (!(src & 0x40)) /* - * Volume IRQ pending - */ - if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* - * Not done - * yet - */ - { - volume_ignore |= voice_bit; - do_volume_irq(voice); - } - } -} - -void guswave_dma_irq(void) -{ - unsigned char status; - - status = gus_look8(0x41); /* Get DMA IRQ Status */ - if (status & 0x40) /* DMA interrupt pending */ - switch (active_device) - { - case GUS_DEV_WAVE: - wake_up(&dram_sleeper); - break; - - case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ - gus_write8(0x41, 0); /* Disable GF1 DMA */ - gus_transfer_output_block(pcm_current_dev, pcm_current_buf, - pcm_current_count, - pcm_current_intrflag, 1); - break; - - case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ - gus_write8(0x41, 0); /* Disable GF1 DMA */ - if (pcm_qlen < pcm_nblk) - { - dma_active = 0; - if (gus_busy) - { - if (audio_devs[gus_devnum]->dmap_out->qlen > 0) - DMAbuf_outputintr(gus_devnum, 0); - } - } - break; - - default: - break; - } - status = gus_look8(0x49); /* - * Get Sampling IRQ Status - */ - if (status & 0x40) /* - * Sampling Irq pending - */ - { - DMAbuf_inputintr(gus_devnum); - } -} - -/* - * Timer stuff - */ - -static volatile int select_addr, data_addr; -static volatile int curr_timer; - -void gus_timer_command(unsigned int addr, unsigned int val) -{ - int i; - - outb(((unsigned char) (addr & 0xff)), select_addr); - - for (i = 0; i < 2; i++) - inb(select_addr); - - outb(((unsigned char) (val & 0xff)), data_addr); - - for (i = 0; i < 2; i++) - inb(select_addr); -} - -static void arm_timer(int timer, unsigned int interval) -{ - curr_timer = timer; - - if (timer == 1) - { - gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */ - gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */ - gus_timer_command(0x04, 0x01); /* Start timer 1 */ - } - else - { - gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */ - gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */ - gus_timer_command(0x04, 0x02); /* Start timer 2 */ - } - - gus_timer_enabled = 1; -} - -static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick) -{ - int timer_no, resolution; - int divisor; - - if (usecs_per_tick > (256 * 80)) - { - timer_no = 2; - resolution = 320; /* usec */ - } - else - { - timer_no = 1; - resolution = 80; /* usec */ - } - divisor = (usecs_per_tick + (resolution / 2)) / resolution; - arm_timer(timer_no, divisor); - - return divisor * resolution; -} - -static void gus_tmr_disable(int dev) -{ - gus_write8(0x45, 0); /* Disable both timers */ - gus_timer_enabled = 0; -} - -static void gus_tmr_restart(int dev) -{ - if (curr_timer == 1) - gus_write8(0x45, 0x04); /* Start timer 1 again */ - else - gus_write8(0x45, 0x08); /* Start timer 2 again */ - gus_timer_enabled = 1; -} - -static struct sound_lowlev_timer gus_tmr = -{ - 0, - 1, - gus_tmr_start, - gus_tmr_disable, - gus_tmr_restart -}; - -static void gus_tmr_install(int io_base) -{ - struct sound_lowlev_timer *tmr; - - select_addr = io_base; - data_addr = io_base + 1; - - tmr = &gus_tmr; - -#ifdef THIS_GETS_FIXED - sound_timer_init(&gus_tmr, "GUS"); -#endif -} diff --git a/sound/oss/harmony.c b/sound/oss/harmony.c deleted file mode 100644 index 6601b284f0..0000000000 --- a/sound/oss/harmony.c +++ /dev/null @@ -1,1330 +0,0 @@ -/* - sound/oss/harmony.c - - This is a sound driver for ASP's and Lasi's Harmony sound chip - and is unlikely to be used for anything other than on a HP PA-RISC. - - Harmony is found in HP 712s, 715/new and many other GSC based machines. - On older 715 machines you'll find the technically identical chip - called 'Vivace'. Both Harmony and Vicace are supported by this driver. - - Copyright 2000 (c) Linuxcare Canada, Alex deVries - Copyright 2000-2003 (c) Helge Deller - Copyright 2001 (c) Matthieu Delahaye - Copyright 2001 (c) Jean-Christophe Vaugeois - Copyright 2004 (c) Stuart Brady - - -TODO: - - fix SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls to - return the real values - - add private ioctl for selecting line- or microphone input - (only one of them is available at the same time) - - add module parameters - - implement mmap functionality - - implement gain meter ? - - ... -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "sound_config.h" - - -#define PFX "harmony: " -#define HARMONY_VERSION "V0.9a" - -#undef DEBUG -#ifdef DEBUG -# define DPRINTK printk -#else -# define DPRINTK(x,...) -#endif - - -#define MAX_BUFS 10 /* maximum number of rotating buffers */ -#define HARMONY_BUF_SIZE 4096 /* needs to be a multiple of PAGE_SIZE (4096)! */ - -#define CNTL_C 0x80000000 -#define CNTL_ST 0x00000020 -#define CNTL_44100 0x00000015 /* HARMONY_SR_44KHZ */ -#define CNTL_8000 0x00000008 /* HARMONY_SR_8KHZ */ - -#define GAINCTL_HE 0x08000000 -#define GAINCTL_LE 0x04000000 -#define GAINCTL_SE 0x02000000 - -#define DSTATUS_PN 0x00000200 -#define DSTATUS_RN 0x00000002 - -#define DSTATUS_IE 0x80000000 - -#define HARMONY_DF_16BIT_LINEAR 0 -#define HARMONY_DF_8BIT_ULAW 1 -#define HARMONY_DF_8BIT_ALAW 2 - -#define HARMONY_SS_MONO 0 -#define HARMONY_SS_STEREO 1 - -#define HARMONY_SR_8KHZ 0x08 -#define HARMONY_SR_16KHZ 0x09 -#define HARMONY_SR_27KHZ 0x0A -#define HARMONY_SR_32KHZ 0x0B -#define HARMONY_SR_48KHZ 0x0E -#define HARMONY_SR_9KHZ 0x0F -#define HARMONY_SR_5KHZ 0x10 -#define HARMONY_SR_11KHZ 0x11 -#define HARMONY_SR_18KHZ 0x12 -#define HARMONY_SR_22KHZ 0x13 -#define HARMONY_SR_37KHZ 0x14 -#define HARMONY_SR_44KHZ 0x15 -#define HARMONY_SR_33KHZ 0x16 -#define HARMONY_SR_6KHZ 0x17 - -/* - * Some magics numbers used to auto-detect file formats - */ - -#define HARMONY_MAGIC_8B_ULAW 1 -#define HARMONY_MAGIC_8B_ALAW 27 -#define HARMONY_MAGIC_16B_LINEAR 3 -#define HARMONY_MAGIC_MONO 1 -#define HARMONY_MAGIC_STEREO 2 - -/* - * Channels Positions in mixer register - */ - -#define GAIN_HE_SHIFT 27 -#define GAIN_HE_MASK ( 1 << GAIN_HE_SHIFT) -#define GAIN_LE_SHIFT 26 -#define GAIN_LE_MASK ( 1 << GAIN_LE_SHIFT) -#define GAIN_SE_SHIFT 25 -#define GAIN_SE_MASK ( 1 << GAIN_SE_SHIFT) -#define GAIN_IS_SHIFT 24 -#define GAIN_IS_MASK ( 1 << GAIN_IS_SHIFT) -#define GAIN_MA_SHIFT 20 -#define GAIN_MA_MASK ( 0x0f << GAIN_MA_SHIFT) -#define GAIN_LI_SHIFT 16 -#define GAIN_LI_MASK ( 0x0f << GAIN_LI_SHIFT) -#define GAIN_RI_SHIFT 12 -#define GAIN_RI_MASK ( 0x0f << GAIN_RI_SHIFT) -#define GAIN_LO_SHIFT 6 -#define GAIN_LO_MASK ( 0x3f << GAIN_LO_SHIFT) -#define GAIN_RO_SHIFT 0 -#define GAIN_RO_MASK ( 0x3f << GAIN_RO_SHIFT) - - -#define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT) -#define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT) -#define MAX_MONITOR_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT) - -#define MIXER_INTERNAL SOUND_MIXER_LINE1 -#define MIXER_LINEOUT SOUND_MIXER_LINE2 -#define MIXER_HEADPHONES SOUND_MIXER_LINE3 - -#define MASK_INTERNAL SOUND_MASK_LINE1 -#define MASK_LINEOUT SOUND_MASK_LINE2 -#define MASK_HEADPHONES SOUND_MASK_LINE3 - -/* - * Channels Mask in mixer register - */ - -#define GAIN_TOTAL_SILENCE 0x00F00FFF -#define GAIN_DEFAULT 0x0FF00000 - - -struct harmony_hpa { - u8 unused000; - u8 id; - u8 teleshare_id; - u8 unused003; - u32 reset; - u32 cntl; - u32 gainctl; - u32 pnxtadd; - u32 pcuradd; - u32 rnxtadd; - u32 rcuradd; - u32 dstatus; - u32 ov; - u32 pio; - u32 unused02c; - u32 unused030[3]; - u32 diag; -}; - -struct harmony_dev { - struct harmony_hpa *hpa; - struct parisc_device *dev; - u32 current_gain; - u32 dac_rate; /* 8000 ... 48000 (Hz) */ - u8 data_format; /* HARMONY_DF_xx_BIT_xxx */ - u8 sample_rate; /* HARMONY_SR_xx_KHZ */ - u8 stereo_select; /* HARMONY_SS_MONO or HARMONY_SS_STEREO */ - int format_initialized :1; - int suspended_playing :1; - int suspended_recording :1; - - int blocked_playing :1; - int blocked_recording :1; - int audio_open :1; - int mixer_open :1; - - wait_queue_head_t wq_play, wq_record; - int first_filled_play; /* first buffer containing data (next to play) */ - int nb_filled_play; - int play_offset; - int first_filled_record; - int nb_filled_record; - - int dsp_unit, mixer_unit; -}; - - -static struct harmony_dev harmony; - - -/* - * Dynamic sound buffer allocation and DMA memory - */ - -struct harmony_buffer { - unsigned char *addr; - dma_addr_t dma_handle; - int dma_coherent; /* Zero if dma_alloc_coherent() fails */ - unsigned int len; -}; - -/* - * Harmony memory buffers - */ - -static struct harmony_buffer played_buf, recorded_buf, silent, graveyard; - - -#define CHECK_WBACK_INV_OFFSET(b,offset,len) \ - do { if (!b.dma_coherent) \ - dma_cache_wback_inv((unsigned long)b.addr+offset,len); \ - } while (0) - - -static int __init harmony_alloc_buffer(struct harmony_buffer *b, - unsigned int buffer_count) -{ - b->len = buffer_count * HARMONY_BUF_SIZE; - b->addr = dma_alloc_coherent(&harmony.dev->dev, - b->len, &b->dma_handle, GFP_KERNEL|GFP_DMA); - if (b->addr && b->dma_handle) { - b->dma_coherent = 1; - DPRINTK(KERN_INFO PFX "coherent memory: 0x%lx, played_buf: 0x%lx\n", - (unsigned long)b->dma_handle, (unsigned long)b->addr); - } else { - b->dma_coherent = 0; - /* kmalloc()ed memory will HPMC on ccio machines ! */ - b->addr = kmalloc(b->len, GFP_KERNEL); - if (!b->addr) { - printk(KERN_ERR PFX "couldn't allocate memory\n"); - return -EBUSY; - } - b->dma_handle = __pa(b->addr); - } - return 0; -} - -static void __exit harmony_free_buffer(struct harmony_buffer *b) -{ - if (!b->addr) - return; - - if (b->dma_coherent) - dma_free_coherent(&harmony.dev->dev, - b->len, b->addr, b->dma_handle); - else - kfree(b->addr); - - memset(b, 0, sizeof(*b)); -} - - - -/* - * Low-Level sound-chip programming - */ - -static void __inline__ harmony_wait_CNTL(void) -{ - /* Wait until we're out of control mode */ - while (gsc_readl(&harmony.hpa->cntl) & CNTL_C) - /* wait */ ; -} - - -static void harmony_update_control(void) -{ - u32 default_cntl; - - /* Set CNTL */ - default_cntl = (CNTL_C | /* The C bit */ - (harmony.data_format << 6) | /* Set the data format */ - (harmony.stereo_select << 5) | /* Stereo select */ - (harmony.sample_rate)); /* Set sample rate */ - harmony.format_initialized = 1; - - /* initialize CNTL */ - gsc_writel(default_cntl, &harmony.hpa->cntl); -} - -static void harmony_set_control(u8 data_format, u8 sample_rate, u8 stereo_select) -{ - harmony.sample_rate = sample_rate; - harmony.data_format = data_format; - harmony.stereo_select = stereo_select; - harmony_update_control(); -} - -static void harmony_set_rate(u8 data_rate) -{ - harmony.sample_rate = data_rate; - harmony_update_control(); -} - -static int harmony_detect_rate(int *freq) -{ - int newrate; - switch (*freq) { - case 8000: newrate = HARMONY_SR_8KHZ; break; - case 16000: newrate = HARMONY_SR_16KHZ; break; - case 27428: newrate = HARMONY_SR_27KHZ; break; - case 32000: newrate = HARMONY_SR_32KHZ; break; - case 48000: newrate = HARMONY_SR_48KHZ; break; - case 9600: newrate = HARMONY_SR_9KHZ; break; - case 5512: newrate = HARMONY_SR_5KHZ; break; - case 11025: newrate = HARMONY_SR_11KHZ; break; - case 18900: newrate = HARMONY_SR_18KHZ; break; - case 22050: newrate = HARMONY_SR_22KHZ; break; - case 37800: newrate = HARMONY_SR_37KHZ; break; - case 44100: newrate = HARMONY_SR_44KHZ; break; - case 33075: newrate = HARMONY_SR_33KHZ; break; - case 6615: newrate = HARMONY_SR_6KHZ; break; - default: newrate = HARMONY_SR_8KHZ; - *freq = 8000; break; - } - return newrate; -} - -static void harmony_set_format(u8 data_format) -{ - harmony.data_format = data_format; - harmony_update_control(); -} - -static void harmony_set_stereo(u8 stereo_select) -{ - harmony.stereo_select = stereo_select; - harmony_update_control(); -} - -static void harmony_disable_interrupts(void) -{ - harmony_wait_CNTL(); - gsc_writel(0, &harmony.hpa->dstatus); -} - -static void harmony_enable_interrupts(void) -{ - harmony_wait_CNTL(); - gsc_writel(DSTATUS_IE, &harmony.hpa->dstatus); -} - -/* - * harmony_silence() - * - * This subroutine fills in a buffer starting at location start and - * silences for length bytes. This references the current - * configuration of the audio format. - * - */ - -static void harmony_silence(struct harmony_buffer *buffer, int start, int length) -{ - u8 silence_char; - - /* Despite what you hear, silence is different in - different audio formats. */ - switch (harmony.data_format) { - case HARMONY_DF_8BIT_ULAW: silence_char = 0x55; break; - case HARMONY_DF_8BIT_ALAW: silence_char = 0xff; break; - case HARMONY_DF_16BIT_LINEAR: /* fall through */ - default: silence_char = 0; - } - - memset(buffer->addr+start, silence_char, length); -} - - -static int harmony_audio_open(struct inode *inode, struct file *file) -{ - if (harmony.audio_open) - return -EBUSY; - - harmony.audio_open = 1; - harmony.suspended_playing = harmony.suspended_recording = 1; - harmony.blocked_playing = harmony.blocked_recording = 0; - harmony.first_filled_play = harmony.first_filled_record = 0; - harmony.nb_filled_play = harmony.nb_filled_record = 0; - harmony.play_offset = 0; - init_waitqueue_head(&harmony.wq_play); - init_waitqueue_head(&harmony.wq_record); - - /* Start off in a balanced mode. */ - harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO); - harmony_update_control(); - harmony.format_initialized = 0; - - /* Clear out all the buffers and flush to cache */ - harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); - CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); - - return 0; -} - -/* - * Release (close) the audio device. - */ - -static int harmony_audio_release(struct inode *inode, struct file *file) -{ - if (!harmony.audio_open) - return -EBUSY; - - harmony.audio_open = 0; - - return 0; -} - -/* - * Read recorded data off the audio device. - */ - -static ssize_t harmony_audio_read(struct file *file, - char *buffer, - size_t size_count, - loff_t *ppos) -{ - int total_count = (int) size_count; - int count = 0; - int buf_to_read; - - while (count24) { - if (copy_from_user(file_header, buffer, sizeof(file_header))) - ret = -EFAULT; - - start_string = four_bytes_to_u32(0); - - if ((file_header[4]==0) && (start_string==0x2E736E64)) { - u32 format; - u32 nb_voices; - u32 speed; - - format = four_bytes_to_u32(12); - nb_voices = four_bytes_to_u32(20); - speed = four_bytes_to_u32(16); - - switch (format) { - case HARMONY_MAGIC_8B_ULAW: - harmony.data_format = HARMONY_DF_8BIT_ULAW; - break; - case HARMONY_MAGIC_8B_ALAW: - harmony.data_format = HARMONY_DF_8BIT_ALAW; - break; - case HARMONY_MAGIC_16B_LINEAR: - harmony.data_format = HARMONY_DF_16BIT_LINEAR; - break; - default: - harmony_set_control(HARMONY_DF_16BIT_LINEAR, - HARMONY_SR_44KHZ, HARMONY_SS_STEREO); - goto out; - } - switch (nb_voices) { - case HARMONY_MAGIC_MONO: - harmony.stereo_select = HARMONY_SS_MONO; - break; - case HARMONY_MAGIC_STEREO: - harmony.stereo_select = HARMONY_SS_STEREO; - break; - default: - harmony.stereo_select = HARMONY_SS_MONO; - break; - } - harmony_set_rate(harmony_detect_rate(&speed)); - harmony.dac_rate = speed; - goto out; - } - } - harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO); -out: - return ret; -} -#undef four_bytes_to_u32 - - -static ssize_t harmony_audio_write(struct file *file, - const char *buffer, - size_t size_count, - loff_t *ppos) -{ - int total_count = (int) size_count; - int count = 0; - int frame_size; - int buf_to_fill; - int fresh_buffer; - - if (!harmony.format_initialized) { - if (harmony_format_auto_detect(buffer, total_count)) - return -EFAULT; - } - - while (count= MAX_BUFS && !harmony.play_offset) { - harmony.blocked_playing = 1; - interruptible_sleep_on(&harmony.wq_play); - harmony.blocked_playing = 0; - } - if (harmony.nb_filled_play+2 >= MAX_BUFS && !harmony.play_offset) - return -EBUSY; - - - buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play); - if (harmony.play_offset) { - buf_to_fill--; - buf_to_fill += MAX_BUFS; - } - buf_to_fill %= MAX_BUFS; - - fresh_buffer = (harmony.play_offset == 0); - - /* Figure out the size of the frame */ - if ((total_count-count) >= HARMONY_BUF_SIZE - harmony.play_offset) { - frame_size = HARMONY_BUF_SIZE - harmony.play_offset; - } else { - frame_size = total_count - count; - /* Clear out the buffer, since there we'll only be - overlaying part of the old buffer with the new one */ - harmony_silence(&played_buf, - HARMONY_BUF_SIZE*buf_to_fill+frame_size+harmony.play_offset, - HARMONY_BUF_SIZE-frame_size-harmony.play_offset); - } - - /* Copy the page to an aligned buffer */ - if (copy_from_user(played_buf.addr +(HARMONY_BUF_SIZE*buf_to_fill) + harmony.play_offset, - buffer+count, frame_size)) - return -EFAULT; - CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset), - frame_size); - - if (fresh_buffer) - harmony.nb_filled_play++; - - count += frame_size; - harmony.play_offset += frame_size; - harmony.play_offset %= HARMONY_BUF_SIZE; - if (harmony.suspended_playing && (harmony.nb_filled_play>=4)) - harmony_enable_interrupts(); - } - - return count; -} - -static unsigned int harmony_audio_poll(struct file *file, - struct poll_table_struct *wait) -{ - unsigned int mask = 0; - - if (file->f_mode & FMODE_READ) { - if (!harmony.suspended_recording) - poll_wait(file, &harmony.wq_record, wait); - if (harmony.nb_filled_record) - mask |= POLLIN | POLLRDNORM; - } - - if (file->f_mode & FMODE_WRITE) { - if (!harmony.suspended_playing) - poll_wait(file, &harmony.wq_play, wait); - if (harmony.nb_filled_play) - mask |= POLLOUT | POLLWRNORM; - } - - return mask; -} - -static int harmony_audio_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - int ival, new_format; - int frag_size, frag_buf; - struct audio_buf_info info; - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *) arg); - - case SNDCTL_DSP_GETCAPS: - ival = DSP_CAP_DUPLEX; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_GETFMTS: - ival = (AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW ); - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_SETFMT: - if (get_user(ival, (int *) arg)) - return -EFAULT; - if (ival != AFMT_QUERY) { - switch (ival) { - case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break; - case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break; - case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break; - default: { - DPRINTK(KERN_WARNING PFX - "unsupported sound format 0x%04x requested.\n", - ival); - ival = AFMT_S16_BE; - return put_user(ival, (int *) arg); - } - } - harmony_set_format(new_format); - return 0; - } else { - switch (harmony.data_format) { - case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break; - case HARMONY_DF_8BIT_ALAW: ival = AFMT_A_LAW; break; - case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break; - default: ival = 0; - } - return put_user(ival, (int *) arg); - } - - case SOUND_PCM_READ_RATE: - ival = harmony.dac_rate; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_SPEED: - if (get_user(ival, (int *) arg)) - return -EFAULT; - harmony_set_rate(harmony_detect_rate(&ival)); - harmony.dac_rate = ival; - return put_user(ival, (int*) arg); - - case SNDCTL_DSP_STEREO: - if (get_user(ival, (int *) arg)) - return -EFAULT; - if (ival != 0 && ival != 1) - return -EINVAL; - harmony_set_stereo(ival); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(ival, (int *) arg)) - return -EFAULT; - if (ival != 1 && ival != 2) { - ival = harmony.stereo_select == HARMONY_SS_MONO ? 1 : 2; - return put_user(ival, (int *) arg); - } - harmony_set_stereo(ival-1); - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - ival = HARMONY_BUF_SIZE; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_RESET: - if (!harmony.suspended_recording) { - /* TODO: stop_recording() */ - } - return 0; - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(ival, (int *)arg)) - return -EFAULT; - frag_size = ival & 0xffff; - frag_buf = (ival>>16) & 0xffff; - /* TODO: We use hardcoded fragment sizes and numbers for now */ - frag_size = 12; /* 4096 == 2^12 */ - frag_buf = MAX_BUFS; - ival = (frag_buf << 16) + frag_size; - return put_user(ival, (int *) arg); - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - info.fragstotal = MAX_BUFS; - info.fragments = MAX_BUFS - harmony.nb_filled_play; - info.fragsize = HARMONY_BUF_SIZE; - info.bytes = info.fragments * info.fragsize; - return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - info.fragstotal = MAX_BUFS; - info.fragments = /*MAX_BUFS-*/ harmony.nb_filled_record; - info.fragsize = HARMONY_BUF_SIZE; - info.bytes = info.fragments * info.fragsize; - return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; - - case SNDCTL_DSP_SYNC: - return 0; - } - - return -EINVAL; -} - - -/* - * harmony_interrupt() - * - * harmony interruption service routine - * - */ - -static irqreturn_t harmony_interrupt(int irq, void *dev, struct pt_regs *regs) -{ - u32 dstatus; - struct harmony_hpa *hpa; - - /* Setup the hpa */ - hpa = ((struct harmony_dev *)dev)->hpa; - harmony_wait_CNTL(); - - /* Read dstatus and pcuradd (the current address) */ - dstatus = gsc_readl(&hpa->dstatus); - - /* Turn off interrupts */ - harmony_disable_interrupts(); - - /* Check if this is a request to get the next play buffer */ - if (dstatus & DSTATUS_PN) { - if (!harmony.nb_filled_play) { - harmony.suspended_playing = 1; - gsc_writel((unsigned long)silent.dma_handle, &hpa->pnxtadd); - - if (!harmony.suspended_recording) - harmony_enable_interrupts(); - } else { - harmony.suspended_playing = 0; - gsc_writel((unsigned long)played_buf.dma_handle + - (HARMONY_BUF_SIZE*harmony.first_filled_play), - &hpa->pnxtadd); - harmony.first_filled_play++; - harmony.first_filled_play %= MAX_BUFS; - harmony.nb_filled_play--; - - harmony_enable_interrupts(); - } - - if (harmony.blocked_playing) - wake_up_interruptible(&harmony.wq_play); - } - - /* Check if we're being asked to fill in a recording buffer */ - if (dstatus & DSTATUS_RN) { - if((harmony.nb_filled_record+2>=MAX_BUFS) || harmony.suspended_recording) - { - harmony.nb_filled_record = 0; - harmony.first_filled_record = 0; - harmony.suspended_recording = 1; - gsc_writel((unsigned long)graveyard.dma_handle, &hpa->rnxtadd); - if (!harmony.suspended_playing) - harmony_enable_interrupts(); - } else { - int buf_to_fill; - buf_to_fill = (harmony.first_filled_record+harmony.nb_filled_record) % MAX_BUFS; - CHECK_WBACK_INV_OFFSET(recorded_buf, HARMONY_BUF_SIZE*buf_to_fill, HARMONY_BUF_SIZE); - gsc_writel((unsigned long)recorded_buf.dma_handle + - HARMONY_BUF_SIZE*buf_to_fill, - &hpa->rnxtadd); - harmony.nb_filled_record++; - harmony_enable_interrupts(); - } - - if (harmony.blocked_recording && harmony.nb_filled_record>3) - wake_up_interruptible(&harmony.wq_record); - } - return IRQ_HANDLED; -} - -/* - * Sound playing functions - */ - -static struct file_operations harmony_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = harmony_audio_read, - .write = harmony_audio_write, - .poll = harmony_audio_poll, - .ioctl = harmony_audio_ioctl, - .open = harmony_audio_open, - .release = harmony_audio_release, -}; - -static int harmony_audio_init(void) -{ - /* Request that IRQ */ - if (request_irq(harmony.dev->irq, harmony_interrupt, 0 ,"harmony", &harmony)) { - printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony.dev->irq); - return -EFAULT; - } - - harmony.dsp_unit = register_sound_dsp(&harmony_audio_fops, -1); - if (harmony.dsp_unit < 0) { - printk(KERN_ERR PFX "Error registering dsp\n"); - free_irq(harmony.dev->irq, &harmony); - return -EFAULT; - } - - /* Clear the buffers so you don't end up with crap in the buffers. */ - harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); - - /* Make sure this makes it to cache */ - CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); - - /* Clear out the silent buffer and flush to cache */ - harmony_silence(&silent, 0, HARMONY_BUF_SIZE); - CHECK_WBACK_INV_OFFSET(silent, 0, HARMONY_BUF_SIZE); - - harmony.audio_open = 0; - - return 0; -} - - -/* - * mixer functions - */ - -static void harmony_mixer_set_gain(void) -{ - harmony_wait_CNTL(); - gsc_writel(harmony.current_gain, &harmony.hpa->gainctl); -} - -/* - * Read gain of selected channel. - * The OSS rate is from 0 (silent) to 100 -> need some conversions - * - * The harmony gain are attenuation for output and monitor gain. - * is amplifaction for input gain - */ -#define to_harmony_level(level,max) ((level)*max/100) -#define to_oss_level(level,max) ((level)*100/max) - -static int harmony_mixer_get_level(int channel) -{ - int left_level; - int right_level; - - switch (channel) { - case SOUND_MIXER_VOLUME: - left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT; - right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT; - left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); - right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); - return (right_level << 8)+left_level; - - case SOUND_MIXER_IGAIN: - left_level = (harmony.current_gain & GAIN_LI_MASK) >> GAIN_LI_SHIFT; - right_level= (harmony.current_gain & GAIN_RI_MASK) >> GAIN_RI_SHIFT; - left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); - right_level= to_oss_level(right_level, MAX_INPUT_LEVEL); - return (right_level << 8)+left_level; - - case SOUND_MIXER_MONITOR: - left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT; - left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL); - return (left_level << 8)+left_level; - } - return -EINVAL; -} - - - -/* - * Some conversions for the same reasons. - * We give back the new real value(s) due to - * the rescale. - */ - -static int harmony_mixer_set_level(int channel, int value) -{ - int left_level; - int right_level; - int new_left_level; - int new_right_level; - - right_level = (value & 0x0000ff00) >> 8; - left_level = value & 0x000000ff; - if (right_level > 100) right_level = 100; - if (left_level > 100) left_level = 100; - - switch (channel) { - case SOUND_MIXER_VOLUME: - right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL); - left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL); - new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); - new_left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); - harmony.current_gain = (harmony.current_gain & ~(GAIN_LO_MASK | GAIN_RO_MASK)) - | (left_level << GAIN_LO_SHIFT) | (right_level << GAIN_RO_SHIFT); - harmony_mixer_set_gain(); - return (new_right_level << 8) + new_left_level; - - case SOUND_MIXER_IGAIN: - right_level = to_harmony_level(right_level, MAX_INPUT_LEVEL); - left_level = to_harmony_level(left_level, MAX_INPUT_LEVEL); - new_right_level = to_oss_level(right_level, MAX_INPUT_LEVEL); - new_left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); - harmony.current_gain = (harmony.current_gain & ~(GAIN_LI_MASK | GAIN_RI_MASK)) - | (left_level << GAIN_LI_SHIFT) | (right_level << GAIN_RI_SHIFT); - harmony_mixer_set_gain(); - return (new_right_level << 8) + new_left_level; - - case SOUND_MIXER_MONITOR: - left_level = to_harmony_level(100-left_level, MAX_MONITOR_LEVEL); - new_left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL); - harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK) | (left_level << GAIN_MA_SHIFT); - harmony_mixer_set_gain(); - return (new_left_level << 8) + new_left_level; - } - - return -EINVAL; -} - -#undef to_harmony_level -#undef to_oss_level - -/* - * Return the selected input device (mic or line) - */ - -static int harmony_mixer_get_recmask(void) -{ - int current_input_line; - - current_input_line = (harmony.current_gain & GAIN_IS_MASK) - >> GAIN_IS_SHIFT; - if (current_input_line) - return SOUND_MASK_MIC; - - return SOUND_MASK_LINE; -} - -/* - * Set the input (only one at time, arbitrary priority to line in) - */ - -static int harmony_mixer_set_recmask(int recmask) -{ - int new_input_line; - int new_input_mask; - int current_input_line; - - current_input_line = (harmony.current_gain & GAIN_IS_MASK) - >> GAIN_IS_SHIFT; - if ((current_input_line && ((recmask & SOUND_MASK_LINE) || !(recmask & SOUND_MASK_MIC))) || - (!current_input_line && ((recmask & SOUND_MASK_LINE) && !(recmask & SOUND_MASK_MIC)))) { - new_input_line = 0; - new_input_mask = SOUND_MASK_LINE; - } else { - new_input_line = 1; - new_input_mask = SOUND_MASK_MIC; - } - harmony.current_gain = ((harmony.current_gain & ~GAIN_IS_MASK) | - (new_input_line << GAIN_IS_SHIFT )); - harmony_mixer_set_gain(); - return new_input_mask; -} - - -/* - * give the active outlines - */ - -static int harmony_mixer_get_outmask(void) -{ - int outmask = 0; - - if (harmony.current_gain & GAIN_SE_MASK) outmask |= MASK_INTERNAL; - if (harmony.current_gain & GAIN_LE_MASK) outmask |= MASK_LINEOUT; - if (harmony.current_gain & GAIN_HE_MASK) outmask |= MASK_HEADPHONES; - - return outmask; -} - - -static int harmony_mixer_set_outmask(int outmask) -{ - if (outmask & MASK_INTERNAL) - harmony.current_gain |= GAIN_SE_MASK; - else - harmony.current_gain &= ~GAIN_SE_MASK; - - if (outmask & MASK_LINEOUT) - harmony.current_gain |= GAIN_LE_MASK; - else - harmony.current_gain &= ~GAIN_LE_MASK; - - if (outmask & MASK_HEADPHONES) - harmony.current_gain |= GAIN_HE_MASK; - else - harmony.current_gain &= ~GAIN_HE_MASK; - - harmony_mixer_set_gain(); - - return (outmask & (MASK_INTERNAL | MASK_LINEOUT | MASK_HEADPHONES)); -} - -/* - * This code is inspired from sb_mixer.c - */ - -static int harmony_mixer_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int val; - int ret; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info, 0, sizeof(info)); - strncpy(info.id, "harmony", sizeof(info.id)-1); - strncpy(info.name, "Harmony audio", sizeof(info.name)-1); - info.modify_counter = 1; /* ? */ - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - - /* read */ - val = 0; - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if (get_user(val, (int *)arg)) - return -EFAULT; - - switch (cmd) { - case MIXER_READ(SOUND_MIXER_CAPS): - ret = SOUND_CAP_EXCL_INPUT; - break; - case MIXER_READ(SOUND_MIXER_STEREODEVS): - ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; - break; - - case MIXER_READ(SOUND_MIXER_RECMASK): - ret = SOUND_MASK_MIC | SOUND_MASK_LINE; - break; - case MIXER_READ(SOUND_MIXER_DEVMASK): - ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN | - SOUND_MASK_MONITOR; - break; - case MIXER_READ(SOUND_MIXER_OUTMASK): - ret = MASK_INTERNAL | MASK_LINEOUT | - MASK_HEADPHONES; - break; - - case MIXER_WRITE(SOUND_MIXER_RECSRC): - ret = harmony_mixer_set_recmask(val); - break; - case MIXER_READ(SOUND_MIXER_RECSRC): - ret = harmony_mixer_get_recmask(); - break; - - case MIXER_WRITE(SOUND_MIXER_OUTSRC): - ret = harmony_mixer_set_outmask(val); - break; - case MIXER_READ(SOUND_MIXER_OUTSRC): - ret = harmony_mixer_get_outmask(); - break; - - case MIXER_WRITE(SOUND_MIXER_VOLUME): - case MIXER_WRITE(SOUND_MIXER_IGAIN): - case MIXER_WRITE(SOUND_MIXER_MONITOR): - ret = harmony_mixer_set_level(cmd & 0xff, val); - break; - - case MIXER_READ(SOUND_MIXER_VOLUME): - case MIXER_READ(SOUND_MIXER_IGAIN): - case MIXER_READ(SOUND_MIXER_MONITOR): - ret = harmony_mixer_get_level(cmd & 0xff); - break; - - default: - return -EINVAL; - } - - if (put_user(ret, (int *)arg)) - return -EFAULT; - return 0; -} - - -static int harmony_mixer_open(struct inode *inode, struct file *file) -{ - if (harmony.mixer_open) - return -EBUSY; - harmony.mixer_open = 1; - return 0; -} - -static int harmony_mixer_release(struct inode *inode, struct file *file) -{ - if (!harmony.mixer_open) - return -EBUSY; - harmony.mixer_open = 0; - return 0; -} - -static struct file_operations harmony_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = harmony_mixer_open, - .release = harmony_mixer_release, - .ioctl = harmony_mixer_ioctl, -}; - - -/* - * Mute all the output and reset Harmony. - */ - -static void __init harmony_mixer_reset(void) -{ - harmony.current_gain = GAIN_TOTAL_SILENCE; - harmony_mixer_set_gain(); - harmony_wait_CNTL(); - gsc_writel(1, &harmony.hpa->reset); - mdelay(50); /* wait 50 ms */ - gsc_writel(0, &harmony.hpa->reset); - harmony.current_gain = GAIN_DEFAULT; - harmony_mixer_set_gain(); -} - -static int __init harmony_mixer_init(void) -{ - /* Register the device file operations */ - harmony.mixer_unit = register_sound_mixer(&harmony_mixer_fops, -1); - if (harmony.mixer_unit < 0) { - printk(KERN_WARNING PFX "Error Registering Mixer Driver\n"); - return -EFAULT; - } - - harmony_mixer_reset(); - harmony.mixer_open = 0; - - return 0; -} - - - -/* - * This is the callback that's called by the inventory hardware code - * if it finds a match to the registered driver. - */ -static int __devinit -harmony_driver_probe(struct parisc_device *dev) -{ - u8 id; - u8 rev; - u32 cntl; - int ret; - - if (harmony.hpa) { - /* We only support one Harmony at this time */ - printk(KERN_ERR PFX "driver already registered\n"); - return -EBUSY; - } - - if (!dev->irq) { - printk(KERN_ERR PFX "no irq found\n"); - return -ENODEV; - } - - /* Set the HPA of harmony */ - harmony.hpa = (struct harmony_hpa *)dev->hpa.start; - harmony.dev = dev; - - /* Grab the ID and revision from the device */ - id = gsc_readb(&harmony.hpa->id); - if ((id | 1) != 0x15) { - printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", id); - return -EBUSY; - } - cntl = gsc_readl(&harmony.hpa->cntl); - rev = (cntl>>20) & 0xff; - - printk(KERN_INFO "Lasi Harmony Audio driver " HARMONY_VERSION ", " - "h/w id %i, rev. %i at 0x%lx, IRQ %i\n", - id, rev, dev->hpa.start, harmony.dev->irq); - - /* Make sure the control bit isn't set, although I don't think it - ever is. */ - if (cntl & CNTL_C) { - printk(KERN_WARNING PFX "CNTL busy\n"); - harmony.hpa = 0; - return -EBUSY; - } - - /* Initialize the memory buffers */ - if (harmony_alloc_buffer(&played_buf, MAX_BUFS) || - harmony_alloc_buffer(&recorded_buf, MAX_BUFS) || - harmony_alloc_buffer(&graveyard, 1) || - harmony_alloc_buffer(&silent, 1)) { - ret = -EBUSY; - goto out_err; - } - - /* Initialize /dev/mixer and /dev/audio */ - if ((ret=harmony_mixer_init())) - goto out_err; - if ((ret=harmony_audio_init())) - goto out_err; - - return 0; - -out_err: - harmony.hpa = 0; - harmony_free_buffer(&played_buf); - harmony_free_buffer(&recorded_buf); - harmony_free_buffer(&graveyard); - harmony_free_buffer(&silent); - return ret; -} - - -static struct parisc_device_id harmony_tbl[] = { - /* { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, Bushmaster/Flounder */ - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, /* 712/715 Audio */ - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, /* Pace Audio */ - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, /* Outfield / Coral II */ - { 0, } -}; - -MODULE_DEVICE_TABLE(parisc, harmony_tbl); - -static struct parisc_driver harmony_driver = { - .name = "Lasi Harmony", - .id_table = harmony_tbl, - .probe = harmony_driver_probe, -}; - -static int __init init_harmony(void) -{ - return register_parisc_driver(&harmony_driver); -} - -static void __exit cleanup_harmony(void) -{ - free_irq(harmony.dev->irq, &harmony); - unregister_sound_mixer(harmony.mixer_unit); - unregister_sound_dsp(harmony.dsp_unit); - harmony_free_buffer(&played_buf); - harmony_free_buffer(&recorded_buf); - harmony_free_buffer(&graveyard); - harmony_free_buffer(&silent); - unregister_parisc_driver(&harmony_driver); -} - - -MODULE_AUTHOR("Alex DeVries "); -MODULE_DESCRIPTION("Harmony sound driver"); -MODULE_LICENSE("GPL"); - -module_init(init_harmony); -module_exit(cleanup_harmony); - diff --git a/sound/oss/ics2101.c b/sound/oss/ics2101.c deleted file mode 100644 index 45918df150..0000000000 --- a/sound/oss/ics2101.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * sound/oss/ics2101.c - * - * Driver for the ICS2101 mixer of GUS v3.7. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init() - */ -#include -#include -#include "sound_config.h" - -#include - -#include "gus.h" -#include "gus_hw.h" - -#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ - SOUND_MASK_SYNTH| \ - SOUND_MASK_CD | SOUND_MASK_VOLUME) - -extern int *gus_osp; -extern int gus_base; -extern spinlock_t gus_lock; -static int volumes[ICS_MIXDEVS]; -static int left_fix[ICS_MIXDEVS] = -{1, 1, 1, 2, 1, 2}; -static int right_fix[ICS_MIXDEVS] = -{2, 2, 2, 1, 2, 1}; - -static int scale_vol(int vol) -{ - /* - * Experimental volume scaling by Risto Kankkunen. - * This should give smoother volume response than just - * a plain multiplication. - */ - - int e; - - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - vol = (31 * vol + 50) / 100; - e = 0; - if (vol) - { - while (vol < 16) - { - vol <<= 1; - e--; - } - vol -= 16; - e += 7; - } - return ((e << 4) + vol); -} - -static void write_mix(int dev, int chn, int vol) -{ - int *selector; - unsigned long flags; - int ctrl_addr = dev << 3; - int attn_addr = dev << 3; - - vol = scale_vol(vol); - - if (chn == CHN_LEFT) - { - selector = left_fix; - ctrl_addr |= 0x00; - attn_addr |= 0x02; - } - else - { - selector = right_fix; - ctrl_addr |= 0x01; - attn_addr |= 0x03; - } - - spin_lock_irqsave(&gus_lock, flags); - outb((ctrl_addr), u_MixSelect); - outb((selector[dev]), u_MixData); - outb((attn_addr), u_MixSelect); - outb(((unsigned char) vol), u_MixData); - spin_unlock_irqrestore(&gus_lock,flags); -} - -static int set_volumes(int dev, int vol) -{ - int left = vol & 0x00ff; - int right = (vol >> 8) & 0x00ff; - - if (left < 0) - left = 0; - if (left > 100) - left = 100; - if (right < 0) - right = 0; - if (right > 100) - right = 100; - - write_mix(dev, CHN_LEFT, left); - write_mix(dev, CHN_RIGHT, right); - - vol = left + (right << 8); - volumes[dev] = vol; - return vol; -} - -static int ics2101_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int val; - - if (((cmd >> 8) & 0xff) == 'M') { - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - - if (get_user(val, (int __user *)arg)) - return -EFAULT; - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - return gus_default_mixer_ioctl(dev, cmd, arg); - - case SOUND_MIXER_MIC: - val = set_volumes(DEV_MIC, val); - break; - - case SOUND_MIXER_CD: - val = set_volumes(DEV_CD, val); - break; - - case SOUND_MIXER_LINE: - val = set_volumes(DEV_LINE, val); - break; - - case SOUND_MIXER_SYNTH: - val = set_volumes(DEV_GF1, val); - break; - - case SOUND_MIXER_VOLUME: - val = set_volumes(DEV_VOL, val); - break; - - default: - return -EINVAL; - } - return put_user(val, (int __user *)arg); - } else { - switch (cmd & 0xff) { - /* - * Return parameters - */ - case SOUND_MIXER_RECSRC: - return gus_default_mixer_ioctl(dev, cmd, arg); - - case SOUND_MIXER_DEVMASK: - val = MIX_DEVS; - break; - - case SOUND_MIXER_STEREODEVS: - val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; - break; - - case SOUND_MIXER_RECMASK: - val = SOUND_MASK_MIC | SOUND_MASK_LINE; - break; - - case SOUND_MIXER_CAPS: - val = 0; - break; - - case SOUND_MIXER_MIC: - val = volumes[DEV_MIC]; - break; - - case SOUND_MIXER_LINE: - val = volumes[DEV_LINE]; - break; - - case SOUND_MIXER_CD: - val = volumes[DEV_CD]; - break; - - case SOUND_MIXER_VOLUME: - val = volumes[DEV_VOL]; - break; - - case SOUND_MIXER_SYNTH: - val = volumes[DEV_GF1]; - break; - - default: - return -EINVAL; - } - return put_user(val, (int __user *)arg); - } - } - return -EINVAL; -} - -static struct mixer_operations ics2101_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "ICS2101", - .name = "ICS2101 Multimedia Mixer", - .ioctl = ics2101_mixer_ioctl -}; - -int __init ics2101_mixer_init(void) -{ - int i; - int n; - - if ((n = sound_alloc_mixerdev()) != -1) - { - mixer_devs[n] = &ics2101_mixer_operations; - - /* - * Some GUS v3.7 cards had some channels flipped. Disable - * the flipping feature if the model id is other than 5. - */ - - if (inb(u_MixSelect) != 5) - { - for (i = 0; i < ICS_MIXDEVS; i++) - left_fix[i] = 1; - for (i = 0; i < ICS_MIXDEVS; i++) - right_fix[i] = 2; - } - set_volumes(DEV_GF1, 0x5a5a); - set_volumes(DEV_CD, 0x5a5a); - set_volumes(DEV_MIC, 0x0000); - set_volumes(DEV_LINE, 0x5a5a); - set_volumes(DEV_VOL, 0x5a5a); - set_volumes(DEV_UNUSED, 0x0000); - } - return n; -} diff --git a/sound/oss/iwmem.h b/sound/oss/iwmem.h deleted file mode 100644 index 48d333c730..0000000000 --- a/sound/oss/iwmem.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * sound/oss/iwmem.h - * - * DRAM size encoding table for AMD Interwave chip. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Bartlomiej Zolnierkiewicz : added __initdata to mem_decode - */ - - -#define K 1024 -#define M (1024*K) -static int mem_decode[][4] __initdata = -{ -/* Bank0 Bank1 Bank2 Bank3 Encoding bits */ - {256*K, 0, 0, 0}, /* 0 */ - {256*K, 256*K, 0, 0}, /* 1 */ - {256*K, 256*K, 256*K, 256*K}, /* 2 */ - {256*K, 1*M, 0, 0}, /* 3 */ - {256*K, 1*M, 1*M, 1*M}, /* 4 */ - {256*K, 256*K, 1*M, 0}, /* 5 */ - {256*K, 256*K, 1*M, 1*M}, /* 6 */ - {1*M, 0, 0, 0}, /* 7 */ - {1*M, 1*M, 0, 0}, /* 8 */ - {1*M, 1*M, 1*M, 1*M}, /* 9 */ - {4*M, 0, 0, 0}, /* 10 */ - {4*M, 4*M, 0, 0}, /* 11 */ - {4*M, 4*M, 4*M, 4*M} /* 12 */ -}; diff --git a/sound/oss/mad16.c b/sound/oss/mad16.c deleted file mode 100644 index aa3c50db66..0000000000 --- a/sound/oss/mad16.c +++ /dev/null @@ -1,1113 +0,0 @@ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * mad16.c - * - * Initialization code for OPTi MAD16 compatible audio chips. Including - * - * OPTi 82C928 MAD16 (replaced by C929) - * OAK OTI-601D Mozart - * OAK OTI-605 Mozart (later version with MPU401 Midi) - * OPTi 82C929 MAD16 Pro - * OPTi 82C930 - * OPTi 82C924 - * - * These audio interface chips don't produce sound themselves. They just - * connect some other components (OPL-[234] and a WSS compatible codec) - * to the PC bus and perform I/O, DMA and IRQ address decoding. There is - * also a UART for the MPU-401 mode (not 82C928/Mozart). - * The Mozart chip appears to be compatible with the 82C928, although later - * issues of the card, using the OTI-605 chip, have an MPU-401 compatible Midi - * port. This port is configured differently to that of the OPTi audio chips. - * - * Changes - * - * Alan Cox Clean up, added module selections. - * - * A. Wik Added support for Opti924 PnP. - * Improved debugging support. 16-May-1998 - * Fixed bug. 16-Jun-1998 - * - * Torsten Duwe Made Opti924 PnP support non-destructive - * 23-Dec-1998 - * - * Paul Grayson Added support for Midi on later Mozart cards. - * 25-Nov-1999 - * Christoph Hellwig Adapted to module_init/module_exit. - * Arnaldo C. de Melo got rid of attach_uart401 21-Sep-2000 - * - * Pavel Rabel Clean up Nov-2000 - */ - -#include -#include -#include -#include -#include -#include "sound_config.h" - -#include "ad1848.h" -#include "sb.h" -#include "mpu401.h" - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -#define SUPPORT_JOYSTICK 1 -#endif - -static int mad16_conf; -static int mad16_cdsel; -static DEFINE_SPINLOCK(lock); - -#define C928 1 -#define MOZART 2 -#define C929 3 -#define C930 4 -#define C924 5 - -/* - * Registers - * - * The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations). - * All ports are inactive by default. They can be activated by - * writing 0xE2 or 0xE3 to the password register. The password is valid - * only until the next I/O read or write. - * - * 82C930 uses 0xE4 as the password and indirect addressing to access - * the config registers. - */ - -#define MC0_PORT 0xf8c /* Dummy port */ -#define MC1_PORT 0xf8d /* SB address, CD-ROM interface type, joystick */ -#define MC2_PORT 0xf8e /* CD-ROM address, IRQ, DMA, plus OPL4 bit */ -#define MC3_PORT 0xf8f -#define PASSWD_REG 0xf8f -#define MC4_PORT 0xf90 -#define MC5_PORT 0xf91 -#define MC6_PORT 0xf92 -#define MC7_PORT 0xf93 -#define MC8_PORT 0xf94 -#define MC9_PORT 0xf95 -#define MC10_PORT 0xf96 -#define MC11_PORT 0xf97 -#define MC12_PORT 0xf98 - -static int board_type = C928; - -static int *mad16_osp; -static int c931_detected; /* minor differences from C930 */ -static char c924pnp; /* " " " C924 */ -static int debug; /* debugging output */ - -#ifdef DDB -#undef DDB -#endif -#define DDB(x) do {if (debug) x;} while (0) - -static unsigned char mad_read(int port) -{ - unsigned long flags; - unsigned char tmp; - - spin_lock_irqsave(&lock,flags); - - switch (board_type) /* Output password */ - { - case C928: - case MOZART: - outb((0xE2), PASSWD_REG); - break; - - case C929: - outb((0xE3), PASSWD_REG); - break; - - case C930: - /* outb(( 0xE4), PASSWD_REG); */ - break; - - case C924: - /* the c924 has its ports relocated by -128 if - PnP is enabled -aw */ - if (!c924pnp) - outb((0xE5), PASSWD_REG); else - outb((0xE5), PASSWD_REG - 0x80); - break; - } - - if (board_type == C930) - { - outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ - tmp = inb(0xe0f); /* Read from data reg */ - } - else - if (!c924pnp) - tmp = inb(port); else - tmp = inb(port-0x80); - spin_unlock_irqrestore(&lock,flags); - - return tmp; -} - -static void mad_write(int port, int value) -{ - unsigned long flags; - - spin_lock_irqsave(&lock,flags); - - switch (board_type) /* Output password */ - { - case C928: - case MOZART: - outb((0xE2), PASSWD_REG); - break; - - case C929: - outb((0xE3), PASSWD_REG); - break; - - case C930: - /* outb(( 0xE4), PASSWD_REG); */ - break; - - case C924: - if (!c924pnp) - outb((0xE5), PASSWD_REG); else - outb((0xE5), PASSWD_REG - 0x80); - break; - } - - if (board_type == C930) - { - outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ - outb(((unsigned char) (value & 0xff)), 0xe0f); - } - else - if (!c924pnp) - outb(((unsigned char) (value & 0xff)), port); else - outb(((unsigned char) (value & 0xff)), port-0x80); - spin_unlock_irqrestore(&lock,flags); -} - -static int __init detect_c930(void) -{ - unsigned char tmp = mad_read(MC1_PORT); - - if ((tmp & 0x06) != 0x06) - { - DDB(printk("Wrong C930 signature (%x)\n", tmp)); - /* return 0; */ - } - mad_write(MC1_PORT, 0); - - if (mad_read(MC1_PORT) != 0x06) - { - DDB(printk("Wrong C930 signature2 (%x)\n", tmp)); - /* return 0; */ - } - mad_write(MC1_PORT, tmp); /* Restore bits */ - - mad_write(MC7_PORT, 0); - if ((tmp = mad_read(MC7_PORT)) != 0) - { - DDB(printk("MC7 not writable (%x)\n", tmp)); - return 0; - } - mad_write(MC7_PORT, 0xcb); - if ((tmp = mad_read(MC7_PORT)) != 0xcb) - { - DDB(printk("MC7 not writable2 (%x)\n", tmp)); - return 0; - } - - tmp = mad_read(MC0_PORT+18); - if (tmp == 0xff || tmp == 0x00) - return 1; - /* We probably have a C931 */ - DDB(printk("Detected C931 config=0x%02x\n", tmp)); - c931_detected = 1; - - /* - * We cannot configure the chip if it is in PnP mode. - * If we have a CSN assigned (bit 8 in MC13) we first try - * a software reset, then a software power off, finally - * Clearing PnP mode. The last option is not - * Bit 8 in MC13 - */ - if ((mad_read(MC0_PORT+13) & 0x80) == 0) - return 1; - - /* Software reset */ - mad_write(MC9_PORT, 0x02); - mad_write(MC9_PORT, 0x00); - - if ((mad_read(MC0_PORT+13) & 0x80) == 0) - return 1; - - /* Power off, and on again */ - mad_write(MC9_PORT, 0xc2); - mad_write(MC9_PORT, 0xc0); - - if ((mad_read(MC0_PORT+13) & 0x80) == 0) - return 1; - -#if 0 - /* Force off PnP mode. This is not recommended because - * the PnP bios will not recognize the chip on the next - * warm boot and may assignd different resources to other - * PnP/PCI cards. - */ - mad_write(MC0_PORT+17, 0x04); -#endif - return 1; -} - -static int __init detect_mad16(void) -{ - unsigned char tmp, tmp2, bit; - int i, port; - - /* - * Check that reading a register doesn't return bus float (0xff) - * when the card is accessed using password. This may fail in case - * the card is in low power mode. Normally at least the power saving - * mode bit should be 0. - */ - - if ((tmp = mad_read(MC1_PORT)) == 0xff) - { - DDB(printk("MC1_PORT returned 0xff\n")); - return 0; - } - for (i = 0xf8d; i <= 0xf98; i++) - if (!c924pnp) - DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))); - else - DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i))); - - if (board_type == C930) - return detect_c930(); - - /* - * Now check that the gate is closed on first I/O after writing - * the password. (This is how a MAD16 compatible card works). - */ - - if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */ - { - DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); - return 0; - } - - bit = (c924pnp) ? 0x20 : 0x80; - port = (c924pnp) ? MC2_PORT : MC1_PORT; - - tmp = mad_read(port); - mad_write(port, tmp ^ bit); /* Toggle a bit */ - if ((tmp2 = mad_read(port)) != (tmp ^ bit)) /* Compare the bit */ - { - mad_write(port, tmp); /* Restore */ - DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); - return 0; - } - mad_write(port, tmp); /* Restore */ - return 1; /* Bingo */ -} - -static int __init wss_init(struct address_info *hw_config) -{ - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00. - */ - - if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 && - (inb(hw_config->io_base + 3) & 0x3f) != 0x00) - { - DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); - return 0; - } - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) - { - printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - if (hw_config->irq > 9 && inb(hw_config->io_base + 3) & 0x80) - printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); - return 1; -} - -static void __init init_c930(struct address_info *hw_config, int base) -{ - unsigned char cfg = 0; - - cfg |= (0x0f & mad16_conf); - - if(c931_detected) - { - /* Bit 0 has reversd meaning. Bits 1 and 2 sese - reversed on write. - Support only IDE cdrom. IDE port programmed - somewhere else. */ - cfg = (cfg & 0x09) ^ 0x07; - } - cfg |= base << 4; - mad_write(MC1_PORT, cfg); - - /* MC2 is CD configuration. Don't touch it. */ - - mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ - - /* bit 2 of MC4 reverses it's meaning between the C930 - and the C931. */ - cfg = c931_detected ? 0x04 : 0x00; - - if(mad16_cdsel & 0x20) - mad_write(MC4_PORT, 0x62|cfg); /* opl4 */ - else - mad_write(MC4_PORT, 0x52|cfg); /* opl3 */ - - mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ - mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ - mad_write(MC7_PORT, 0xCB); - mad_write(MC10_PORT, 0x11); -} - -static int __init chip_detect(void) -{ - int i; - - /* - * Then try to detect with the old password - */ - board_type = C924; - - DDB(printk("Detect using password = 0xE5\n")); - - if (detect_mad16()) { - return 1; - } - - board_type = C928; - - DDB(printk("Detect using password = 0xE2\n")); - - if (detect_mad16()) - { - unsigned char model; - - if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { - DDB(printk("mad16.c: Mozart detected\n")); - board_type = MOZART; - } else { - DDB(printk("mad16.c: 82C928 detected???\n")); - board_type = C928; - } - return 1; - } - - board_type = C929; - - DDB(printk("Detect using password = 0xE3\n")); - - if (detect_mad16()) - { - DDB(printk("mad16.c: 82C929 detected\n")); - return 1; - } - - if (inb(PASSWD_REG) != 0xff) - return 0; - - /* - * First relocate MC# registers to 0xe0e/0xe0f, disable password - */ - - outb((0xE4), PASSWD_REG); - outb((0x80), PASSWD_REG); - - board_type = C930; - - DDB(printk("Detect using password = 0xE4\n")); - - for (i = 0xf8d; i <= 0xf93; i++) - DDB(printk("port %03x = %02x\n", i, mad_read(i))); - - if(detect_mad16()) { - DDB(printk("mad16.c: 82C930 detected\n")); - return 1; - } - - /* The C931 has the password reg at F8D */ - outb((0xE4), 0xF8D); - outb((0x80), 0xF8D); - DDB(printk("Detect using password = 0xE4 for C931\n")); - - if (detect_mad16()) { - return 1; - } - - board_type = C924; - c924pnp++; - DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n")); - if (detect_mad16()) { - DDB(printk("mad16.c: 82C924 PnP detected\n")); - return 1; - } - - c924pnp=0; - - return 0; -} - -static int __init probe_mad16(struct address_info *hw_config) -{ - int i; - unsigned char tmp; - unsigned char cs4231_mode = 0; - - int ad_flags = 0; - - signed char bits; - - static char dma_bits[4] = { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; - int dma = hw_config->dma, dma2 = hw_config->dma2; - unsigned char dma2_bit = 0; - int base; - struct resource *ports; - - mad16_osp = hw_config->osp; - - switch (hw_config->io_base) { - case 0x530: - base = 0; - break; - case 0xe80: - base = 1; - break; - case 0xf40: - base = 2; - break; - case 0x604: - base = 3; - break; - default: - printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); - return 0; - } - - if (dma != 0 && dma != 1 && dma != 3) { - printk(KERN_ERR "MSS: Bad DMA %d\n", dma); - return 0; - } - - /* - * Check that all ports return 0xff (bus float) when no password - * is written to the password register. - */ - - DDB(printk("--- Detecting MAD16 / Mozart ---\n")); - if (!chip_detect()) - return 0; - - switch (hw_config->irq) { - case 7: - bits = 8; - break; - case 9: - bits = 0x10; - break; - case 10: - bits = 0x18; - break; - case 12: - bits = 0x20; - break; - case 5: /* Also IRQ5 is possible on C930 */ - if (board_type == C930 || c924pnp) { - bits = 0x28; - break; - } - default: - printk(KERN_ERR "MAD16/Mozart: Bad IRQ %d\n", hw_config->irq); - return 0; - } - - ports = request_region(hw_config->io_base + 4, 4, "ad1848"); - if (!ports) { - printk(KERN_ERR "MSS: I/O port conflict\n"); - return 0; - } - if (!request_region(hw_config->io_base, 4, "mad16 WSS config")) { - release_region(hw_config->io_base + 4, 4); - printk(KERN_ERR "MSS: I/O port conflict\n"); - return 0; - } - - if (board_type == C930) { - init_c930(hw_config, base); - goto got_it; - } - - for (i = 0xf8d; i <= 0xf93; i++) { - if (!c924pnp) - DDB(printk("port %03x = %02x\n", i, mad_read(i))); - else - DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i))); - } - -/* - * Set the WSS address - */ - - tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ - tmp |= base << 4; /* WSS port select bits */ - - /* - * Set optional CD-ROM and joystick settings. - */ - - tmp &= ~0x0f; - tmp |= (mad16_conf & 0x0f); /* CD-ROM and joystick bits */ - mad_write(MC1_PORT, tmp); - - tmp = mad16_cdsel; - mad_write(MC2_PORT, tmp); - mad_write(MC3_PORT, 0xf0); /* Disable SB */ - - if (board_type == C924) /* Specific C924 init values */ - { - mad_write(MC4_PORT, 0xA0); - mad_write(MC5_PORT, 0x05); - mad_write(MC6_PORT, 0x03); - } - if (!ad1848_detect(ports, &ad_flags, mad16_osp)) - goto fail; - - if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) - cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ - - if (board_type == C929) - { - mad_write(MC4_PORT, 0xa2); - mad_write(MC5_PORT, 0xA5 | cs4231_mode); - mad_write(MC6_PORT, 0x03); /* Disable MPU401 */ - } - else - { - mad_write(MC4_PORT, 0x02); - mad_write(MC5_PORT, 0x30 | cs4231_mode); - } - - for (i = 0xf8d; i <= 0xf93; i++) { - if (!c924pnp) - DDB(printk("port %03x after init = %02x\n", i, mad_read(i))); - else - DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); - } - -got_it: - ad_flags = 0; - if (!ad1848_detect(ports, &ad_flags, mad16_osp)) - goto fail; - - if (!wss_init(hw_config)) - goto fail; - - /* - * Set the IRQ and DMA addresses. - */ - - outb((bits | 0x40), config_port); - if ((inb(version_port) & 0x40) == 0) - printk(KERN_ERR "[IRQ Conflict?]\n"); - - /* - * Handle the capture DMA channel - */ - - if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) - { - if (!((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0))) - { /* Unsupported combination. Try to swap channels */ - int tmp = dma; - - dma = dma2; - dma2 = tmp; - } - if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0)) - { - dma2_bit = 0x04; /* Enable capture DMA */ - } - else - { - printk("MAD16: Invalid capture DMA\n"); - dma2 = dma; - } - } - else dma2 = dma; - - outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - - hw_config->slots[0] = ad1848_init("mad16 WSS", ports, - hw_config->irq, - dma, - dma2, 0, - hw_config->osp, - THIS_MODULE); - return 1; - -fail: - release_region(hw_config->io_base + 4, 4); - release_region(hw_config->io_base, 4); - return 0; -} - -static int __init probe_mad16_mpu(struct address_info *hw_config) -{ - unsigned char tmp; - - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { - -#ifdef CONFIG_MAD16_OLDCARD - - tmp = mad_read(MC3_PORT); - - /* - * MAD16 SB base is defined by the WSS base. It cannot be changed - * alone. - * Ignore configured I/O base. Use the active setting. - */ - - if (mad_read(MC1_PORT) & 0x20) - hw_config->io_base = 0x240; - else - hw_config->io_base = 0x220; - - switch (hw_config->irq) - { - case 5: - tmp = (tmp & 0x3f) | 0x80; - break; - case 7: - tmp = (tmp & 0x3f); - break; - case 11: - tmp = (tmp & 0x3f) | 0x40; - break; - default: - printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n"); - return 0; - } - - mad_write(MC3_PORT, tmp | 0x04); - hw_config->driver_use_1 = SB_MIDI_ONLY; - if (!request_region(hw_config->io_base, 16, "soundblaster")) - return 0; - if (!sb_dsp_detect(hw_config, 0, 0, NULL)) { - release_region(hw_config->io_base, 16); - return 0; - } - - if (mad_read(MC1_PORT) & 0x20) - hw_config->io_base = 0x240; - else - hw_config->io_base = 0x220; - - hw_config->name = "Mad16/Mozart"; - sb_dsp_init(hw_config, THIS_MODULE); - return 1; -#else - /* assuming all later Mozart cards are identified as - * either 82C928 or Mozart. If so, following code attempts - * to set MPU register. TODO - add probing - */ - - tmp = mad_read(MC8_PORT); - - switch (hw_config->irq) - { - case 5: - tmp |= 0x08; - break; - case 7: - tmp |= 0x10; - break; - case 9: - tmp |= 0x18; - break; - case 10: - tmp |= 0x20; - break; - case 11: - tmp |= 0x28; - break; - default: - printk(KERN_ERR "mad16/MOZART: invalid mpu_irq\n"); - return 0; - } - - switch (hw_config->io_base) - { - case 0x300: - tmp |= 0x01; - break; - case 0x310: - tmp |= 0x03; - break; - case 0x320: - tmp |= 0x05; - break; - case 0x330: - tmp |= 0x07; - break; - default: - printk(KERN_ERR "mad16/MOZART: invalid mpu_io\n"); - return 0; - } - - mad_write(MC8_PORT, tmp); /* write MPU port parameters */ - goto probe_401; -#endif - } - tmp = mad_read(MC6_PORT) & 0x83; - tmp |= 0x80; /* MPU-401 enable */ - - /* Set the MPU base bits */ - - switch (hw_config->io_base) - { - case 0x300: - tmp |= 0x60; - break; - case 0x310: - tmp |= 0x40; - break; - case 0x320: - tmp |= 0x20; - break; - case 0x330: - tmp |= 0x00; - break; - default: - printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base); - return 0; - } - - /* Set the MPU IRQ bits */ - - switch (hw_config->irq) - { - case 5: - tmp |= 0x10; - break; - case 7: - tmp |= 0x18; - break; - case 9: - tmp |= 0x00; - break; - case 10: - tmp |= 0x08; - break; - default: - printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq); - break; - } - - mad_write(MC6_PORT, tmp); /* Write MPU401 config */ - -#ifndef CONFIG_MAD16_OLDCARD -probe_401: -#endif - hw_config->driver_use_1 = SB_MIDI_ONLY; - hw_config->name = "Mad16/Mozart"; - return probe_uart401(hw_config, THIS_MODULE); -} - -static void __exit unload_mad16(struct address_info *hw_config) -{ - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0); - release_region(hw_config->io_base, 4); - sound_unload_audiodev(hw_config->slots[0]); -} - -static void __exit unload_mad16_mpu(struct address_info *hw_config) -{ -#ifdef CONFIG_MAD16_OLDCARD - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { - sb_dsp_unload(hw_config, 0); - return; - } -#endif - - unload_uart401(hw_config); -} - -static struct address_info cfg; -static struct address_info cfg_mpu; - -static int found_mpu; - -static int __initdata mpu_io = 0; -static int __initdata mpu_irq = 0; -static int __initdata io = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ -static int __initdata irq = -1; -static int __initdata cdtype = 0; -static int __initdata cdirq = 0; -static int __initdata cdport = 0x340; -static int __initdata cddma = -1; -static int __initdata opl4 = 0; -static int __initdata joystick = 0; - -module_param(mpu_io, int, 0); -module_param(mpu_irq, int, 0); -module_param(io, int, 0); -module_param(dma, int, 0); -module_param(dma16, int, 0); -module_param(irq, int, 0); -module_param(cdtype, int, 0); -module_param(cdirq, int, 0); -module_param(cdport, int, 0); -module_param(cddma, int, 0); -module_param(opl4, int, 0); -module_param(joystick, bool, 0); -module_param(debug, bool, 0644); - -static int __initdata dma_map[2][8] = -{ - {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02}, - {0x03, -1, 0x01, 0x00, -1, -1, -1, -1} -}; - -static int __initdata irq_map[16] = -{ - 0x00, -1, -1, 0x0A, - -1, 0x04, -1, 0x08, - -1, 0x10, 0x14, 0x18, - -1, -1, -1, -1 -}; - -#ifdef SUPPORT_JOYSTICK - -static struct gameport *gameport; - -static int __devinit mad16_register_gameport(int io_port) -{ - if (!request_region(io_port, 1, "mad16 gameport")) { - printk(KERN_ERR "mad16: gameport address 0x%#x already in use\n", io_port); - return -EBUSY; - } - - gameport = gameport_allocate_port(); - if (!gameport) { - printk(KERN_ERR "mad16: can not allocate memory for gameport\n"); - release_region(io_port, 1); - return -ENOMEM; - } - - gameport_set_name(gameport, "MAD16 Gameport"); - gameport_set_phys(gameport, "isa%04x/gameport0", io_port); - gameport->io = io_port; - - gameport_register_port(gameport); - - return 0; -} - -static inline void mad16_unregister_gameport(void) -{ - if (gameport) { - /* the gameport was initialized so we must free it up */ - gameport_unregister_port(gameport); - gameport = NULL; - release_region(0x201, 1); - } -} -#else -static inline int mad16_register_gameport(int io_port) { return -ENOSYS; } -static inline void mad16_unregister_gameport(void) { } -#endif - -static int __devinit init_mad16(void) -{ - int dmatype = 0; - - printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - printk(KERN_INFO "CDROM "); - switch (cdtype) - { - case 0x00: - printk("Disabled"); - cdirq = 0; - break; - case 0x02: - printk("Sony CDU31A"); - dmatype = 1; - if(cddma == -1) cddma = 3; - break; - case 0x04: - printk("Mitsumi"); - dmatype = 0; - if(cddma == -1) cddma = 5; - break; - case 0x06: - printk("Panasonic Lasermate"); - dmatype = 1; - if(cddma == -1) cddma = 3; - break; - case 0x08: - printk("Secondary IDE"); - dmatype = 0; - if(cddma == -1) cddma = 5; - break; - case 0x0A: - printk("Primary IDE"); - dmatype = 0; - if(cddma == -1) cddma = 5; - break; - default: - printk("\n"); - printk(KERN_ERR "Invalid CDROM type\n"); - return -EINVAL; - } - - /* - * Build the config words - */ - - mad16_conf = (joystick ^ 1) | cdtype; - mad16_cdsel = 0; - if (opl4) - mad16_cdsel |= 0x20; - - if(cdtype){ - if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) - { - printk("\n"); - printk(KERN_ERR "Invalid CDROM DMA\n"); - return -EINVAL; - } - if (cddma) - printk(", DMA %d", cddma); - else - printk(", no DMA"); - - if (!cdirq) - printk(", no IRQ"); - else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) - { - printk(", invalid IRQ (disabling)"); - cdirq = 0; - } - else printk(", IRQ %d", cdirq); - - mad16_cdsel |= dma_map[dmatype][cddma]; - - if (cdtype < 0x08) - { - switch (cdport) - { - case 0x340: - mad16_cdsel |= 0x00; - break; - case 0x330: - mad16_cdsel |= 0x40; - break; - case 0x360: - mad16_cdsel |= 0x80; - break; - case 0x320: - mad16_cdsel |= 0xC0; - break; - default: - printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); - return -EINVAL; - } - } - mad16_cdsel |= irq_map[cdirq]; - } - - printk(".\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); - return -EINVAL; - } - - if (!request_region(MC0_PORT, 12, "mad16")) - return -EBUSY; - - if (!probe_mad16(&cfg)) { - release_region(MC0_PORT, 12); - return -ENODEV; - } - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - found_mpu = probe_mad16_mpu(&cfg_mpu); - - if (joystick) - mad16_register_gameport(0x201); - - return 0; -} - -static void __exit cleanup_mad16(void) -{ - if (found_mpu) - unload_mad16_mpu(&cfg_mpu); - mad16_unregister_gameport(); - unload_mad16(&cfg); - release_region(MC0_PORT, 12); -} - -module_init(init_mad16); -module_exit(cleanup_mad16); - -#ifndef MODULE -static int __init setup_mad16(char *str) -{ - /* io, irq */ - int ints[8]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - mpu_io = ints[5]; - mpu_irq = ints[6]; - joystick = ints[7]; - - return 1; -} - -__setup("mad16=", setup_mad16); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/maestro.c b/sound/oss/maestro.c deleted file mode 100644 index 1d98d100d7..0000000000 --- a/sound/oss/maestro.c +++ /dev/null @@ -1,3686 +0,0 @@ -/***************************************************************************** - * - * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x - * - * 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. - * - * (c) Copyright 1999 Alan Cox - * - * Based heavily on SonicVibes.c: - * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Heavily modified by Zach Brown based on lunch - * with ESS engineers. Many thanks to Howard Kim for providing - * contacts and hardware. Honorable mention goes to Eric - * Brombaugh for all sorts of things. Best regards to the - * proprietors of Hack Central for fine lodging. - * - * Supported devices: - * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * - * Hardware Description - * - * A working Maestro setup contains the Maestro chip wired to a - * codec or 2. In the Maestro we have the APUs, the ASSP, and the - * Wavecache. The APUs can be though of as virtual audio routing - * channels. They can take data from a number of sources and perform - * basic encodings of the data. The wavecache is a storehouse for - * PCM data. Typically it deals with PCI and interracts with the - * APUs. The ASSP is a wacky DSP like device that ESS is loth - * to release docs on. Thankfully it isn't required on the Maestro - * until you start doing insane things like FM emulation and surround - * encoding. The codecs are almost always AC-97 compliant codecs, - * but it appears that early Maestros may have had PT101 (an ESS - * part?) wired to them. The only real difference in the Maestro - * families is external goop like docking capability, memory for - * the ASSP, and initialization differences. - * - * Driver Operation - * - * We only drive the APU/Wavecache as typical DACs and drive the - * mixers in the codecs. There are 64 APUs. We assign 6 to each - * /dev/dsp? device. 2 channels for output, and 4 channels for - * input. - * - * Each APU can do a number of things, but we only really use - * 3 basic functions. For playback we use them to convert PCM - * data fetched over PCI by the wavecahche into analog data that - * is handed to the codec. One APU for mono, and a pair for stereo. - * When in stereo, the combination of smarts in the APU and Wavecache - * decide which wavecache gets the left or right channel. - * - * For record we still use the old overly mono system. For each in - * coming channel the data comes in from the codec, through a 'input' - * APU, through another rate converter APU, and then into memory via - * the wavecache and PCI. If its stereo, we mash it back into LRLR in - * software. The pass between the 2 APUs is supposedly what requires us - * to have a 512 byte buffer sitting around in wavecache/memory. - * - * The wavecache makes our life even more fun. First off, it can - * only address the first 28 bits of PCI address space, making it - * useless on quite a few architectures. Secondly, its insane. - * It claims to fetch from 4 regions of PCI space, each 4 meg in length. - * But that doesn't really work. You can only use 1 region. So all our - * allocations have to be in 4meg of each other. Booo. Hiss. - * So we have a module parameter, dsps_order, that is the order of - * the number of dsps to provide. All their buffer space is allocated - * on open time. The sonicvibes OSS routines we inherited really want - * power of 2 buffers, so we have all those next to each other, then - * 512 byte regions for the recording wavecaches. This ends up - * wasting quite a bit of memory. The only fixes I can see would be - * getting a kernel allocator that could work in zones, or figuring out - * just how to coerce the WP into doing what we want. - * - * The indirection of the various registers means we have to spinlock - * nearly all register accesses. We have the main register indirection - * like the wave cache, maestro registers, etc. Then we have beasts - * like the APU interface that is indirect registers gotten at through - * the main maestro indirection. Ouch. We spinlock around the actual - * ports on a per card basis. This means spinlock activity at each IO - * operation, but the only IO operation clusters are in non critical - * paths and it makes the code far easier to follow. Interrupts are - * blocked while holding the locks because the int handler has to - * get at some of them :(. The mixer interface doesn't, however. - * We also have an OSS state lock that is thrown around in a few - * places. - * - * This driver has brute force APM suspend support. We catch suspend - * notifications and stop all work being done on the chip. Any people - * that try between this shutdown and the real suspend operation will - * be put to sleep. When we resume we restore our software state on - * the chip and wake up the people that were using it. The code thats - * being used now is quite dirty and assumes we're on a uni-processor - * machine. Much of it will need to be cleaned up for SMP ACPI or - * similar. - * - * We also pay attention to PCI power management now. The driver - * will power down units of the chip that it knows aren't needed. - * The WaveProcessor and company are only powered on when people - * have /dev/dsp*s open. On removal the driver will - * power down the maestro entirely. There could still be - * trouble with BIOSen that magically change power states - * themselves, but we'll see. - * - * History - * v0.15 - May 21 2001 - Marcus Meissner - * Ported to Linux 2.4 PCI API. Some clean ups, global devs list - * removed (now using pci device driver data). - * PM needs to be polished still. Bumped version. - * (still kind of v0.14) May 13 2001 - Ben Pfaff - * Add support for 978 docking and basic hardware volume control - * (still kind of v0.14) Nov 23 - Alan Cox - * Add clocking= for people with seriously warped hardware - * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz - * add __init to maestro_ac97_init() and maestro_install() - * (still based on v0.14) Mar 29 2000 - Zach Brown - * move to 2.3 power management interface, which - * required hacking some suspend/resume/check paths - * make static compilation work - * v0.14 - Jan 28 2000 - Zach Brown - * add PCI power management through ACPI regs. - * we now shut down on machine reboot/halt - * leave scary PCI config items alone (isa stuff, mostly) - * enable 1921s, it seems only mine was broke. - * fix swapped left/right pcm dac. har har. - * up bob freq, increase buffers, fix pointers at underflow - * silly compilation problems - * v0.13 - Nov 18 1999 - Zach Brown - * fix nec Versas? man would that be cool. - * v0.12 - Nov 12 1999 - Zach Brown - * brown bag volume max fix.. - * v0.11 - Nov 11 1999 - Zach Brown - * use proper stereo apu decoding, mmap/write should work. - * make volume sliders more useful, tweak rate calculation. - * fix lame 8bit format reporting bug. duh. apm apu saving buglet also - * fix maestro 1 clock freq "bug", remove pt101 support - * v0.10 - Oct 28 1999 - Zach Brown - * aha, so, sometimes the WP writes a status word to offset 0 - * from one of the PCMBARs. rearrange allocation accordingly.. - * cheers again to Eric for being a good hacker in investigating this. - * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) - * v0.09 - Oct 23 1999 - Zach Brown - * added APM support. - * re-order something such that some 2Es now work. Magic! - * new codec reset routine. made some codecs come to life. - * fix clear_advance, sync some control with ESS. - * now write to all base regs to be paranoid. - * v0.08 - Oct 20 1999 - Zach Brown - * Fix initial buflen bug. I am so smart. also smp compiling.. - * I owe Eric yet another beer: fixed recmask, igain, - * muting, and adc sync consistency. Go Team. - * v0.07 - Oct 4 1999 - Zach Brown - * tweak adc/dac, formating, and stuff to allow full duplex - * allocate dsps memory at open() so we can fit in the wavecache window - * fix wavecache braindamage. again. no more scribbling? - * fix ess 1921 codec bug on some laptops. - * fix dumb pci scanning bug - * started 2.3 cleanup, redid spinlocks, little cleanups - * v0.06 - Sep 20 1999 - Zach Brown - * fix wavecache thinkos. limit to 1 /dev/dsp. - * eric is wearing his thinking toque this week. - * spotted apu mode bugs and gain ramping problem - * don't touch weird mixer regs, make recmask optional - * fixed igain inversion, defaults for mixers, clean up rec_start - * make mono recording work. - * report subsystem stuff, please send reports. - * littles: parallel out, amp now - * v0.05 - Sep 17 1999 - Zach Brown - * merged and fixed up Eric's initial recording code - * munged format handling to catch misuse, needs rewrite. - * revert ring bus init, fixup shared int, add pci busmaster setting - * fix mixer oss interface, fix mic mute and recmask - * mask off unsupported mixers, reset with all 1s, modularize defaults - * make sure bob is running while we need it - * got rid of device limit, initial minimal apm hooks - * pull out dead code/includes, only allow multimedia/audio maestros - * v0.04 - Sep 01 1999 - Zach Brown - * copied memory leak fix from sonicvibes driver - * different ac97 reset, play with 2.0 ac97, simplify ring bus setup - * bob freq code, region sanity, jitter sync fix; all from Eric - * - * TODO - * fix bob frequency - * endianness - * do smart things with ac97 2.0 bits. - * dual codecs - * leave 54->61 open - * - * it also would be fun to have a mode that would not use pci dma at all - * but would copy into the wavecache on board memory and use that - * on architectures that don't like the maestro's pci dma ickiness. - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include -#include -#include - -#include "maestro.h" - -static struct pci_driver maestro_pci_driver; - -/* --------------------------------------------------------------------- */ - -#define M_DEBUG 1 - -#ifdef M_DEBUG -static int debug; -#define M_printk(args...) {if (debug) printk(args);} -#else -#define M_printk(x) -#endif - -/* we try to setup 2^(dsps_order) /dev/dsp devices */ -static int dsps_order; -/* whether or not we mess around with power management */ -static int use_pm=2; /* set to 1 for force */ -/* clocking for broken hardware - a few laptops seem to use a 50Khz clock - ie insmod with clocking=50000 or so */ - -static int clocking=48000; - -MODULE_AUTHOR("Zach Brown , Alan Cox "); -MODULE_DESCRIPTION("ESS Maestro Driver"); -MODULE_LICENSE("GPL"); - -#ifdef M_DEBUG -module_param(debug, bool, 0644); -#endif -module_param(dsps_order, int, 0); -module_param(use_pm, int, 0); -module_param(clocking, int, 0); - -/* --------------------------------------------------------------------- */ -#define DRIVER_VERSION "0.15" - -#ifndef PCI_VENDOR_ESS -#define PCI_VENDOR_ESS 0x125D -#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ -#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ - -#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, - the people the maestro - was bought from */ -#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ -#endif /* PCI_VENDOR_ESS */ - -#define ESS_CHAN_HARD 0x100 - -/* NEC Versas ? */ -#define NEC_VERSA_SUBID1 0x80581033 -#define NEC_VERSA_SUBID2 0x803c1033 - - -/* changed so that I could actually find all the - references and fix them up. it's a little more readable now. */ -#define ESS_FMT_STEREO 0x01 -#define ESS_FMT_16BIT 0x02 -#define ESS_FMT_MASK 0x03 -#define ESS_DAC_SHIFT 0 -#define ESS_ADC_SHIFT 4 - -#define ESS_STATE_MAGIC 0x125D1968 -#define ESS_CARD_MAGIC 0x19283746 - -#define DAC_RUNNING 1 -#define ADC_RUNNING 2 - -#define MAX_DSP_ORDER 2 -#define MAX_DSPS (1<src buffer page */ - void *mixbuf; - -}; - -struct ess_card { - unsigned int magic; - - /* We keep maestro cards in a linked list */ - struct ess_card *next; - - int dev_mixer; - - int card_type; - - /* as most of this is static, - perhaps it should be a pointer to a global struct */ - struct mixer_goo { - int modcnt; - int supported_mixers; - int stereo_mixers; - int record_sources; - /* the caller must guarantee arg sanity before calling these */ -/* int (*read_mixer)(struct ess_card *card, int index);*/ - void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); - int (*recmask_io)(struct ess_card *card,int rw,int mask); - unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; - } mix; - - int power_regs; - - int in_suspend; - wait_queue_head_t suspend_queue; - - struct ess_state channels[MAX_DSPS]; - u16 maestro_map[NR_IDRS]; /* Register map */ - /* we have to store this junk so that we can come back from a - suspend */ - u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ - - /* this locks around the physical registers on the card */ - spinlock_t lock; - - /* memory for this card.. wavecache limited :(*/ - void *dmapages; - int dmaorder; - - /* hardware resources */ - struct pci_dev *pcidev; - u32 iobase; - u32 irq; - - int bob_freq; - char dsps_open; - - int dock_mute_vol; -}; - -static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); - -static unsigned -ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - - -/* --------------------------------------------------------------------- */ - -static void check_suspend(struct ess_card *card); - -/* --------------------------------------------------------------------- */ - - -/* - * ESS Maestro AC97 codec programming interface. - */ - -static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) -{ - int io = card->iobase; - int i; - /* - * Wait for the codec bus to be free - */ - - check_suspend(card); - - for(i=0;i<10000;i++) - { - if(!(inb(io+ESS_AC97_INDEX)&1)) - break; - } - /* - * Write the bus - */ - outw(val, io+ESS_AC97_DATA); - mdelay(1); - outb(cmd, io+ESS_AC97_INDEX); - mdelay(1); -} - -static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) -{ - int io = card->iobase; - int sanity=10000; - u16 data; - int i; - - check_suspend(card); - /* - * Wait for the codec bus to be free - */ - - for(i=0;i<10000;i++) - { - if(!(inb(io+ESS_AC97_INDEX)&1)) - break; - } - - outb(cmd|0x80, io+ESS_AC97_INDEX); - mdelay(1); - - while(inb(io+ESS_AC97_INDEX)&1) - { - sanity--; - if(!sanity) - { - printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); - return 0; - } - } - data=inw(io+ESS_AC97_DATA); - mdelay(1); - return data; -} - -/* OSS interface to the ac97s.. */ - -#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ - SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ - SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) - -#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ - SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ - SOUND_MASK_SPEAKER) - -#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ - SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ - SOUND_MASK_PHONEIN) - -#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<offset); - - if(AC97_STEREO_MASK & (1<> 8) & 0x7f; - right = val & 0x7f; - - if (mixer == SOUND_MIXER_IGAIN) { - right = (right * 100) / mh->scale; - left = (left * 100) / mh->scale; - } else { - right = 100 - ((right * 100) / mh->scale); - left = 100 - ((left * 100) / mh->scale); - } - - ret = left | (right << 8); - } else if (mixer == SOUND_MIXER_SPEAKER) { - ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_MIC) { - ret = 100 - (((val & 0x1f) * 100) / mh->scale); - /* the low bit is optional in the tone sliders and masking - it lets is avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_TREBLE) { - ret = 100 - (((val & 0xe) * 100) / mh->scale); - } - - M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); - - return ret; -} -#endif - -/* write the OSS encoded volume to the given OSS encoded mixer, - again caller's job to make sure all is well in arg land, - call with spinlock held */ - -/* linear scale -> log */ -static unsigned char lin2log[101] = -{ -0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , -50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , -63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , -72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , -78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , -83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , -87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , -90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , -93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , -95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , -97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 -}; - -static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) -{ - u16 val=0; - struct ac97_mixer_hw *mh = &ac97_hw[mixer]; - - M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); - - if(AC97_STEREO_MASK & (1<scale) / 100; - left = (left * mh->scale) / 100; - if ((left == 0) && (right == 0)) - val |= 0x8000; - } else if (mixer == SOUND_MIXER_PCM || mixer == SOUND_MIXER_CD) { - /* log conversion seems bad for them */ - if ((left == 0) && (right == 0)) - val = 0x8000; - right = ((100 - right) * mh->scale) / 100; - left = ((100 - left) * mh->scale) / 100; - } else { - /* log conversion for the stereo controls */ - if((left == 0) && (right == 0)) - val = 0x8000; - right = ((100 - lin2log[right]) * mh->scale) / 100; - left = ((100 - lin2log[left]) * mh->scale) / 100; - } - - val |= (left << 8) | right; - - } else if (mixer == SOUND_MIXER_SPEAKER) { - val = (((100 - left) * mh->scale) / 100) << 1; - } else if (mixer == SOUND_MIXER_MIC) { - val = maestro_ac97_get(card, mh->offset) & ~0x801f; - val |= (((100 - left) * mh->scale) / 100); - /* the low bit is optional in the tone sliders and masking - it lets is avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - val = maestro_ac97_get(card , mh->offset) & ~0x0f00; - val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; - } else if (mixer == SOUND_MIXER_TREBLE) { - val = maestro_ac97_get(card , mh->offset) & ~0x000f; - val |= (((100 - left) * mh->scale) / 100) & 0x000e; - } - - maestro_ac97_set(card , mh->offset, val); - - M_printk(" -> %x\n",val); -} - -/* the following tables allow us to go from - OSS <-> ac97 quickly. */ - -enum ac97_recsettings { - AC97_REC_MIC=0, - AC97_REC_CD, - AC97_REC_VIDEO, - AC97_REC_AUX, - AC97_REC_LINE, - AC97_REC_STEREO, /* combination of all enabled outputs.. */ - AC97_REC_MONO, /*.. or the mono equivalent */ - AC97_REC_PHONE -}; - -static unsigned int ac97_oss_mask[] = { - [AC97_REC_MIC] = SOUND_MASK_MIC, - [AC97_REC_CD] = SOUND_MASK_CD, - [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, - [AC97_REC_AUX] = SOUND_MASK_LINE1, - [AC97_REC_LINE] = SOUND_MASK_LINE, - [AC97_REC_PHONE] = SOUND_MASK_PHONEIN -}; - -/* indexed by bit position */ -static unsigned int ac97_oss_rm[] = { - [SOUND_MIXER_MIC] = AC97_REC_MIC, - [SOUND_MIXER_CD] = AC97_REC_CD, - [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, - [SOUND_MIXER_LINE1] = AC97_REC_AUX, - [SOUND_MIXER_LINE] = AC97_REC_LINE, - [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE -}; - -/* read or write the recmask - the ac97 can really have left and right recording - inputs independently set, but OSS doesn't seem to - want us to express that to the user. - the caller guarantees that we have a supported bit set, - and they must be holding the card's spinlock */ -static int -ac97_recmask_io(struct ess_card *card, int read, int mask) -{ - unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; - - if (read) return val; - - /* oss can have many inputs, maestro can't. try - to pick the 'new' one */ - - if (mask != val) mask &= ~val; - - val = ffs(mask) - 1; - val = ac97_oss_rm[val]; - val |= val << 8; /* set both channels */ - - M_printk("maestro: setting ac97 recmask to 0x%x\n",val); - - maestro_ac97_set(card,0x1a,val); - - return 0; -}; - -/* - * The Maestro can be wired to a standard AC97 compliant codec - * (see www.intel.com for the pdf's on this), or to a PT101 codec - * which appears to be the ES1918 (data sheet on the esstech.com.tw site) - * - * The PT101 setup is untested. - */ - -static u16 __init maestro_ac97_init(struct ess_card *card) -{ - u16 vend1, vend2, caps; - - card->mix.supported_mixers = AC97_SUPPORTED_MASK; - card->mix.stereo_mixers = AC97_STEREO_MASK; - card->mix.record_sources = AC97_RECORD_MASK; -/* card->mix.read_mixer = ac97_read_mixer;*/ - card->mix.write_mixer = ac97_write_mixer; - card->mix.recmask_io = ac97_recmask_io; - - vend1 = maestro_ac97_get(card, 0x7c); - vend2 = maestro_ac97_get(card, 0x7e); - - caps = maestro_ac97_get(card, 0x00); - - printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", - vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); - - if (! (caps & 0x4) ) { - /* no bass/treble nobs */ - card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); - } - - /* XXX endianness, dork head. */ - /* vendor specifc bits.. */ - switch ((long)(vend1 << 16) | vend2) { - case 0x545200ff: /* TriTech */ - /* no idea what this does */ - maestro_ac97_set(card,0x2a,0x0001); - maestro_ac97_set(card,0x2c,0x0000); - maestro_ac97_set(card,0x2c,0xffff); - break; -#if 0 /* i thought the problems I was seeing were with - the 1921, but apparently they were with the pci board - it was on, so this code is commented out. - lets see if this holds true. */ - case 0x83847609: /* ESS 1921 */ - /* writing to 0xe (mic) or 0x1a (recmask) seems - to hang this codec */ - card->mix.supported_mixers &= ~(SOUND_MASK_MIC); - card->mix.record_sources = 0; - card->mix.recmask_io = NULL; -#if 0 /* don't ask. I have yet to see what these actually do. */ - maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ - udelay(20); - maestro_ac97_set(card,0x78,0x3002); - udelay(20); - maestro_ac97_set(card,0x78,0x3802); - udelay(20); -#endif - break; -#endif - default: break; - } - - maestro_ac97_set(card, 0x1E, 0x0404); - /* null misc stuff */ - maestro_ac97_set(card, 0x20, 0x0000); - - return 0; -} - -#if 0 /* there has been 1 person on the planet with a pt101 that we - know of. If they care, they can put this back in :) */ -static u16 maestro_pt101_init(struct ess_card *card,int iobase) -{ - printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); - /* who knows.. */ - maestro_ac97_set(iobase, 0x2A, 0x0001); - maestro_ac97_set(iobase, 0x2C, 0x0000); - maestro_ac97_set(iobase, 0x2C, 0xFFFF); - maestro_ac97_set(iobase, 0x10, 0x9F1F); - maestro_ac97_set(iobase, 0x12, 0x0808); - maestro_ac97_set(iobase, 0x14, 0x9F1F); - maestro_ac97_set(iobase, 0x16, 0x9F1F); - maestro_ac97_set(iobase, 0x18, 0x0404); - maestro_ac97_set(iobase, 0x1A, 0x0000); - maestro_ac97_set(iobase, 0x1C, 0x0000); - maestro_ac97_set(iobase, 0x02, 0x0404); - maestro_ac97_set(iobase, 0x04, 0x0808); - maestro_ac97_set(iobase, 0x0C, 0x801F); - maestro_ac97_set(iobase, 0x0E, 0x801F); - return 0; -} -#endif - -/* this is very magic, and very slow.. */ -static void -maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) -{ - u16 save_68; - u16 w; - u32 vend; - - outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); - outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); - outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); - - /* reset the first codec */ - outw(0x0000, ioaddr+0x36); - save_68 = inw(ioaddr+0x68); - pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ - pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); - if( w & 0x1) - save_68 |= 0x10; - outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ - outw(0x0001, ioaddr + 0x68); - outw(0x0000, ioaddr + 0x60); - udelay(20); - outw(0x0001, ioaddr + 0x60); - mdelay(20); - - outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ - outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); - outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); - outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); - - /* now the second codec */ - outw(0x0000, ioaddr+0x36); - outw(0xfff7, ioaddr + 0x64); - save_68 = inw(ioaddr+0x68); - outw(0x0009, ioaddr + 0x68); - outw(0x0001, ioaddr + 0x60); - udelay(20); - outw(0x0009, ioaddr + 0x60); - mdelay(500); /* .. ouch.. */ - outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); - outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); - outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); - -#if 0 /* the loop here needs to be much better if we want it.. */ - M_printk("trying software reset\n"); - /* try and do a software reset */ - outb(0x80|0x7c, ioaddr + 0x30); - for (w=0; ; w++) { - if ((inw(ioaddr+ 0x30) & 1) == 0) { - if(inb(ioaddr + 0x32) !=0) break; - - outb(0x80|0x7d, ioaddr + 0x30); - if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; - outb(0x80|0x7f, ioaddr + 0x30); - if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; - } - - if( w > 10000) { - outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ - mdelay(500); /* oh my.. */ - outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); - udelay(1); - outw( 0x80, ioaddr+0x30); - for(w = 0 ; w < 10000; w++) { - if((inw(ioaddr + 0x30) & 1) ==0) break; - } - } - } -#endif - if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { - /* turn on external amp? */ - outw(0xf9ff, ioaddr + 0x64); - outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); - outw(0x0209, ioaddr + 0x60); - } - - /* Turn on the 978 docking chip. - First frob the "master output enable" bit, - then set most of the playback volume control registers to max. */ - outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); - outb(0xff, ioaddr+0xc3); - outb(0xff, ioaddr+0xc4); - outb(0xff, ioaddr+0xc6); - outb(0xff, ioaddr+0xc8); - outb(0x3f, ioaddr+0xcf); - outb(0x3f, ioaddr+0xd0); -} -/* - * Indirect register access. Not all registers are readable so we - * need to keep register state ourselves - */ - -#define WRITEABLE_MAP 0xEFFFFF -#define READABLE_MAP 0x64003F - -/* - * The Maestro engineers were a little indirection happy. These indirected - * registers themselves include indirect registers at another layer - */ - -static void __maestro_write(struct ess_card *card, u16 reg, u16 data) -{ - long ioaddr = card->iobase; - - outw(reg, ioaddr+0x02); - outw(data, ioaddr+0x00); - if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); - else card->maestro_map[reg]=data; - -} - -static void maestro_write(struct ess_state *s, u16 reg, u16 data) -{ - unsigned long flags; - - check_suspend(s->card); - spin_lock_irqsave(&s->card->lock,flags); - - __maestro_write(s->card,reg,data); - - spin_unlock_irqrestore(&s->card->lock,flags); -} - -static u16 __maestro_read(struct ess_card *card, u16 reg) -{ - long ioaddr = card->iobase; - - outw(reg, ioaddr+0x02); - return card->maestro_map[reg]=inw(ioaddr+0x00); -} - -static u16 maestro_read(struct ess_state *s, u16 reg) -{ - if(READABLE_MAP & (1<card); - spin_lock_irqsave(&s->card->lock,flags); - - __maestro_read(s->card,reg); - - spin_unlock_irqrestore(&s->card->lock,flags); - } - return s->card->maestro_map[reg]; -} - -/* - * These routines handle accessing the second level indirections to the - * wave ram. - */ - -/* - * The register names are the ones ESS uses (see 104T31.ZIP) - */ - -#define IDR0_DATA_PORT 0x00 -#define IDR1_CRAM_POINTER 0x01 -#define IDR2_CRAM_DATA 0x02 -#define IDR3_WAVE_DATA 0x03 -#define IDR4_WAVE_PTR_LOW 0x04 -#define IDR5_WAVE_PTR_HI 0x05 -#define IDR6_TIMER_CTRL 0x06 -#define IDR7_WAVE_ROMRAM 0x07 - -static void apu_index_set(struct ess_card *card, u16 index) -{ - int i; - __maestro_write(card, IDR1_CRAM_POINTER, index); - for(i=0;i<1000;i++) - if(__maestro_read(card, IDR1_CRAM_POINTER)==index) - return; - printk(KERN_WARNING "maestro: APU register select failed.\n"); -} - -static void apu_data_set(struct ess_card *card, u16 data) -{ - int i; - for(i=0;i<1000;i++) - { - if(__maestro_read(card, IDR0_DATA_PORT)==data) - return; - __maestro_write(card, IDR0_DATA_PORT, data); - } -} - -/* - * This is the public interface for APU manipulation. It handles the - * interlock to avoid two APU writes in parallel etc. Don't diddle - * directly with the stuff above. - */ - -static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) -{ - unsigned long flags; - - check_suspend(s->card); - - if(channel&ESS_CHAN_HARD) - channel&=~ESS_CHAN_HARD; - else - { - if(channel>5) - printk("BAD CHANNEL %d.\n",channel); - else - channel = s->apu[channel]; - /* store based on real hardware apu/reg */ - s->card->apu_map[channel][reg]=data; - } - reg|=(channel<<4); - - /* hooray for double indirection!! */ - spin_lock_irqsave(&s->card->lock,flags); - - apu_index_set(s->card, reg); - apu_data_set(s->card, data); - - spin_unlock_irqrestore(&s->card->lock,flags); -} - -static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) -{ - unsigned long flags; - u16 v; - - check_suspend(s->card); - - if(channel&ESS_CHAN_HARD) - channel&=~ESS_CHAN_HARD; - else - channel = s->apu[channel]; - - reg|=(channel<<4); - - spin_lock_irqsave(&s->card->lock,flags); - - apu_index_set(s->card, reg); - v=__maestro_read(s->card, IDR0_DATA_PORT); - - spin_unlock_irqrestore(&s->card->lock,flags); - return v; -} - - -/* - * The wavecache buffers between the APUs and - * pci bus mastering - */ - -static void wave_set_register(struct ess_state *s, u16 reg, u16 value) -{ - long ioaddr = s->card->iobase; - unsigned long flags; - check_suspend(s->card); - - spin_lock_irqsave(&s->card->lock,flags); - - outw(reg, ioaddr+0x10); - outw(value, ioaddr+0x12); - - spin_unlock_irqrestore(&s->card->lock,flags); -} - -static u16 wave_get_register(struct ess_state *s, u16 reg) -{ - long ioaddr = s->card->iobase; - unsigned long flags; - u16 value; - check_suspend(s->card); - - spin_lock_irqsave(&s->card->lock,flags); - outw(reg, ioaddr+0x10); - value=inw(ioaddr+0x12); - spin_unlock_irqrestore(&s->card->lock,flags); - - return value; -} - -static void sound_reset(int ioaddr) -{ - outw(0x2000, 0x18+ioaddr); - udelay(1); - outw(0x0000, 0x18+ioaddr); - udelay(1); -} - -/* sets the play formats of these apus, should be passed the already shifted format */ -static void set_apu_fmt(struct ess_state *s, int apu, int mode) -{ - int apu_fmt = 0x10; - - if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; - if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; - s->apu_mode[apu] = apu_fmt; - s->apu_mode[apu+1] = apu_fmt; -} - -/* this only fixes the output apu mode to be later set by start_dac and - company. output apu modes are set in ess_rec_setup */ -static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) -{ - s->fmt = (s->fmt & mask) | data; - set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); -} - -/* this is off by a little bit.. */ -static u32 compute_rate(struct ess_state *s, u32 freq) -{ - u32 clock = clock_freq[s->card->card_type]; - - freq = (freq * clocking)/48000; - - if (freq == 48000) - return 0x10000; - - return ((freq / clock) <<16 )+ - (((freq % clock) << 16) / clock); -} - -static void set_dac_rate(struct ess_state *s, unsigned int rate) -{ - u32 freq; - int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - - s->ratedac = rate; - - if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) - rate >>= 1; - -/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ - - freq = compute_rate(s, rate); - - /* Load the frequency, turn on 6dB */ - apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 0, 3, freq>>8); - apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 1, 3, freq>>8); -} - -static void set_adc_rate(struct ess_state *s, unsigned rate) -{ - u32 freq; - - /* Sample Rate conversion APUs don't like 0x10000 for their rate */ - if (rate > 47999) - rate = 47999; - if (rate < 4000) - rate = 4000; - - s->rateadc = rate; - - freq = compute_rate(s, rate); - - /* Load the frequency, turn on 6dB */ - apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 2, 3, freq>>8); - apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 3, 3, freq>>8); - - /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ - freq = 0x10000; - - apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 4, 3, freq>>8); - apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| - ( ((freq&0xFF)<<8)|0x10 )); - apu_set_register(s, 5, 3, freq>>8); -} - -/* Stop our host of recording apus */ -static inline void stop_adc(struct ess_state *s) -{ - /* XXX lets hope we don't have to lock around this */ - if (! (s->enable & ADC_RUNNING)) return; - - s->enable &= ~ADC_RUNNING; - apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); - apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); - apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); - apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); -} - -/* stop output apus */ -static void stop_dac(struct ess_state *s) -{ - /* XXX have to lock around this? */ - if (! (s->enable & DAC_RUNNING)) return; - - s->enable &= ~DAC_RUNNING; - apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); - apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); -} - -static void start_dac(struct ess_state *s) -{ - /* XXX locks? */ - if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && - s->dma_dac.ready && - (! (s->enable & DAC_RUNNING)) ) { - - s->enable |= DAC_RUNNING; - - apu_set_register(s, 0, 0, - (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); - - if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) - apu_set_register(s, 1, 0, - (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); - } -} - -static void start_adc(struct ess_state *s) -{ - /* XXX locks? */ - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { - - s->enable |= ADC_RUNNING; - apu_set_register(s, 2, 0, - (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); - apu_set_register(s, 4, 0, - (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); - - if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { - apu_set_register(s, 3, 0, - (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); - apu_set_register(s, 5, 0, - (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); - } - - } -} - - -/* - * Native play back driver - */ - -/* the mode passed should be already shifted and masked */ -static void -ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) -{ - u32 pa; - u32 tmpval; - int high_apu = 0; - int channel; - - M_printk("mode=%d rate=%d buf=%p len=%d.\n", - mode, rate, buffer, size); - - /* all maestro sizes are in 16bit words */ - size >>=1; - - if(mode&ESS_FMT_STEREO) { - high_apu++; - /* only 16/stereo gets size divided */ - if(mode&ESS_FMT_16BIT) - size>>=1; - } - - for(channel=0; channel <= high_apu; channel++) - { - pa = virt_to_bus(buffer); - - /* set the wavecache control reg */ - tmpval = (pa - 0x10) & 0xFFF8; - if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; - if(mode & ESS_FMT_STEREO) tmpval |= 2; - ess->apu_base[channel]=tmpval; - wave_set_register(ess, ess->apu[channel]<<3, tmpval); - - pa -= virt_to_bus(ess->card->dmapages); - pa>>=1; /* words */ - - /* base offset of dma calcs when reading the pointer - on the left one */ - if(!channel) ess->dma_dac.base = pa&0xFFFF; - - pa|=0x00400000; /* System RAM */ - - /* XXX the 16bit here might not be needed.. */ - if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { - if(channel) - pa|=0x00800000; /* Stereo */ - pa>>=1; - } - -/* XXX think about endianess when writing these registers */ - M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); - /* start of sample */ - apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); - apu_set_register(ess, channel, 5, pa&0xFFFF); - /* sample end */ - apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); - /* setting loop len == sample len */ - apu_set_register(ess, channel, 7, size); - - /* clear effects/env.. */ - apu_set_register(ess, channel, 8, 0x0000); - /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ - apu_set_register(ess, channel, 9, 0xD000); - - /* clear routing stuff */ - apu_set_register(ess, channel, 11, 0x0000); - /* dma on, no envelopes, filter to all 1s) */ - apu_set_register(ess, channel, 0, 0x400F); - - if(mode&ESS_FMT_16BIT) - ess->apu_mode[channel]=0x10; - else - ess->apu_mode[channel]=0x30; - - if(mode&ESS_FMT_STEREO) { - /* set panning: left or right */ - apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); - ess->apu_mode[channel] += 0x10; - } else - apu_set_register(ess, channel, 10, 0x8F08); - } - - /* clear WP interrupts */ - outw(1, ess->card->iobase+0x04); - /* enable WP ints */ - outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); - - /* go team! */ - set_dac_rate(ess,rate); - start_dac(ess); -} - -/* - * Native record driver - */ - -/* again, passed mode is alrady shifted/masked */ -static void -ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) -{ - int apu_step = 2; - int channel; - - M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", - mode, rate, buffer, size); - - /* all maestro sizes are in 16bit words */ - size >>=1; - - /* we're given the full size of the buffer, but - in stereo each channel will only use its half */ - if(mode&ESS_FMT_STEREO) { - size >>=1; - apu_step = 1; - } - - /* APU assignments: 2 = mono/left SRC - 3 = right SRC - 4 = mono/left Input Mixer - 5 = right Input Mixer */ - for(channel=2;channel<6;channel+=apu_step) - { - int i; - int bsize, route; - u32 pa; - u32 tmpval; - - /* data seems to flow from the codec, through an apu into - the 'mixbuf' bit of page, then through the SRC apu - and out to the real 'buffer'. ok. sure. */ - - if(channel & 0x04) { - /* ok, we're an input mixer going from adc - through the mixbuf to the other apus */ - - if(!(channel & 0x01)) { - pa = virt_to_bus(ess->mixbuf); - } else { - pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); - } - - /* we source from a 'magic' apu */ - bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ - route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ - ess->apu_mode[channel] = 0x90; /* Input Mixer */ - - } else { - /* we're a rate converter taking - input from the input apus and outputing it to - system memory */ - if(!(channel & 0x01)) { - pa = virt_to_bus(buffer); - } else { - /* right channel records its split half. - *2 accommodates for rampant shifting earlier */ - pa = virt_to_bus(buffer + size*2); - } - - ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ - - bsize = size; - /* get input from inputing apu */ - route = channel + 2; - } - - M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); - - /* set the wavecache control reg */ - tmpval = (pa - 0x10) & 0xFFF8; - ess->apu_base[channel]=tmpval; - wave_set_register(ess, ess->apu[channel]<<3, tmpval); - - pa -= virt_to_bus(ess->card->dmapages); - pa>>=1; /* words */ - - /* base offset of dma calcs when reading the pointer - on this left one */ - if(channel==2) ess->dma_adc.base = pa&0xFFFF; - - pa|=0x00400000; /* bit 22 -> System RAM */ - - M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", - ess->apu[channel], pa, bsize, route); - - /* Begin loading the APU */ - for(i=0;i<15;i++) /* clear all PBRs */ - apu_set_register(ess, channel, i, 0x0000); - - apu_set_register(ess, channel, 0, 0x400F); - - /* need to enable subgroups.. and we should probably - have different groups for different /dev/dsps.. */ - apu_set_register(ess, channel, 2, 0x8); - - /* Load the buffer into the wave engine */ - apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); - /* XXX reg is little endian.. */ - apu_set_register(ess, channel, 5, pa&0xFFFF); - apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); - apu_set_register(ess, channel, 7, bsize); - - /* clear effects/env.. */ - apu_set_register(ess, channel, 8, 0x00F0); - - /* amplitude now? sure. why not. */ - apu_set_register(ess, channel, 9, 0x0000); - - /* set filter tune, radius, polar pan */ - apu_set_register(ess, channel, 10, 0x8F08); - - /* route input */ - apu_set_register(ess, channel, 11, route); - } - - /* clear WP interrupts */ - outw(1, ess->card->iobase+0x04); - /* enable WP ints */ - outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); - - /* let 'er rip */ - set_adc_rate(ess,rate); - start_adc(ess); -} -/* --------------------------------------------------------------------- */ - -static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) -{ - M_printk("set_dmaa??\n"); -} - -static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) -{ - M_printk("set_dmac??\n"); -} - -/* Playback pointer */ -static inline unsigned get_dmaa(struct ess_state *s) -{ - int offset; - - offset = apu_get_register(s,0,5); - -/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ - - offset-=s->dma_dac.base; - - return (offset&0xFFFE)<<1; /* hardware is in words */ -} - -/* Record pointer */ -static inline unsigned get_dmac(struct ess_state *s) -{ - int offset; - - offset = apu_get_register(s,2,5); - -/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ - - /* The offset is an address not a position relative to base */ - offset-=s->dma_adc.base; - - return (offset&0xFFFE)<<1; /* hardware is in words */ -} - -/* - * Meet Bob, the timer... - */ - -static irqreturn_t ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); - -static void stop_bob(struct ess_state *s) -{ - /* Mask IDR 11,17 */ - maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); - maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); -} - -/* eventually we could be clever and limit bob ints - to the frequency at which our smallest duration - chunks may expire */ -#define ESS_SYSCLK 50000000 -static void start_bob(struct ess_state *s) -{ - int prescale; - int divide; - - /* XXX make freq selector much smarter, see calc_bob_rate */ - int freq = 200; - - /* compute ideal interrupt frequency for buffer size & play rate */ - /* first, find best prescaler value to match freq */ - for(prescale=5;prescale<12;prescale++) - if(freq > (ESS_SYSCLK>>(prescale+9))) - break; - - /* next, back off prescaler whilst getting divider into optimum range */ - divide=1; - while((prescale > 5) && (divide<32)) - { - prescale--; - divide <<=1; - } - divide>>=1; - - /* now fine-tune the divider for best match */ - for(;divide<31;divide++) - if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) - break; - - /* divide = 0 is illegal, but don't let prescale = 4! */ - if(divide == 0) - { - divide++; - if(prescale>5) - prescale--; - } - - maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ - - /* Now set IDR 11/17 */ - maestro_write(s, 0x11, maestro_read(s, 0x11)|1); - maestro_write(s, 0x17, maestro_read(s, 0x17)|1); -} -/* --------------------------------------------------------------------- */ - -/* this quickly calculates the frequency needed for bob - and sets it if its different than what bob is - currently running at. its called often so - needs to be fairly quick. */ -#define BOB_MIN 50 -#define BOB_MAX 400 -static void calc_bob_rate(struct ess_state *s) { -#if 0 /* this thing tries to set the frequency of bob such that - there are 2 interrupts / buffer walked by the dac/adc. That - is probably very wrong for people who actually care about - mid buffer positioning. it should be calculated as bytes/interrupt - and that needs to be decided :) so for now just use the static 150 - in start_bob.*/ - - unsigned int dac_rate=2,adc_rate=1,newrate; - static int israte=-1; - - if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; - else { - dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / - (s->dma_dac.fragsize) ; - } - - if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; - else { - adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / - (s->dma_adc.fragsize) ; - } - - if(dac_rate > adc_rate) newrate = adc_rate; - else newrate=dac_rate; - - if(newrate > BOB_MAX) newrate = BOB_MAX; - else { - if(newrate < BOB_MIN) - newrate = BOB_MIN; - } - - if( israte != newrate) { - printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); - israte=newrate; - } -#endif - -} - -static int -prog_dmabuf(struct ess_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - unsigned bytepersec; - unsigned bufs; - unsigned char fmt; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - fmt = s->fmt; - if (rec) { - stop_adc(s); - fmt >>= ESS_ADC_SHIFT; - } else { - stop_dac(s); - fmt >>= ESS_DAC_SHIFT; - } - spin_unlock_irqrestore(&s->lock, flags); - fmt &= ESS_FMT_MASK; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - - /* this algorithm is a little nuts.. where did /1000 come from? */ - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - - M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); - - memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); - - spin_lock_irqsave(&s->lock, flags); - if (rec) - ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); - else - ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); - - spin_unlock_irqrestore(&s->lock, flags); - db->ready = 1; - - return 0; -} - -static __inline__ void -clear_advance(struct ess_state *s) -{ - unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; - - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - /* account for wrapping? */ - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); -} - -/* call with spinlock held! */ -static void -ess_update_ptr(struct ess_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - /* oh boy should this all be re-written. everything in the current code paths think - that the various counters/pointers are expressed in bytes to the user but we have - two apus doing stereo stuff so we fix it up here.. it propagates to all the various - counters from here. */ - if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { - hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; - } else { - hwptr = get_dmac(s) % s->dma_adc.dmasize; - } - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - /* FILL ME - wrindir(s, SV_CIENABLE, s->enable); */ - stop_adc(s); - /* brute force everyone back in sync, sigh */ - s->dma_adc.count = 0; - s->dma_adc.swptr = 0; - s->dma_adc.hwptr = 0; - s->dma_adc.error++; - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmaa(s) % s->dma_dac.dmasize; - /* the apu only reports the length it has seen, not the - length of the memory that has been used (the WP - knows that) */ - if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) - hwptr<<=1; - - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; -/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { - wake_up(&s->dma_dac.wait); - } - } else { - s->dma_dac.count -= diff; -/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ - if (s->dma_dac.count <= 0) { - M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, - hwptr, s->dma_dac.swptr); - /* FILL ME - wrindir(s, SV_CIENABLE, s->enable); */ - /* XXX how on earth can calling this with the lock held work.. */ - stop_dac(s); - /* brute force everyone back in sync, sigh */ - s->dma_dac.count = 0; - s->dma_dac.swptr = hwptr; - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { - wake_up(&s->dma_dac.wait); -/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, - hwptr);*/ - } - } - } -} - -static irqreturn_t -ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct ess_state *s; - struct ess_card *c = (struct ess_card *)dev_id; - int i; - u32 event; - - if ( ! (event = inb(c->iobase+0x1A)) ) - return IRQ_NONE; - - outw(inw(c->iobase+4)&1, c->iobase+4); - -/* M_printk("maestro int: %x\n",event);*/ - if(event&(1<<6)) - { - int x; - enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt; - int volume; - - /* Figure out which volume control button was pushed, - based on differences from the default register - values. */ - x = inb(c->iobase+0x1c); - if (x&1) vol_evt = MUTE_EVT; - else if (((x>>1)&7) > 4) vol_evt = UP_EVT; - else vol_evt = DOWN_EVT; - - /* Reset the volume control registers. */ - outb(0x88, c->iobase+0x1c); - outb(0x88, c->iobase+0x1d); - outb(0x88, c->iobase+0x1e); - outb(0x88, c->iobase+0x1f); - - /* Deal with the button press in a hammer-handed - manner by adjusting the master mixer volume. */ - volume = c->mix.mixer_state[0] & 0xff; - if (vol_evt == UP_EVT) { - volume += 5; - if (volume > 100) - volume = 100; - } - else if (vol_evt == DOWN_EVT) { - volume -= 5; - if (volume < 0) - volume = 0; - } else { - /* vol_evt == MUTE_EVT */ - if (volume == 0) - volume = c->dock_mute_vol; - else { - c->dock_mute_vol = volume; - volume = 0; - } - } - set_mixer (c, 0, (volume << 8) | volume); - } - - /* Ack all the interrupts. */ - outb(0xFF, c->iobase+0x1A); - - /* - * Update the pointers for all APU's we are running. - */ - for(i=0;ichannels[i]; - if(s->dev_audio == -1) - break; - spin_lock(&s->lock); - ess_update_ptr(s); - spin_unlock(&s->lock); - } - return IRQ_HANDLED; -} - - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; - -#define VALIDATE_MAGIC(FOO,MAG) \ -({ \ - if (!(FOO) || (FOO)->magic != MAG) { \ - printk(invalid_magic,__FUNCTION__); \ - return -ENXIO; \ - } \ -}) - -#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) -#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) - -static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) -{ - unsigned int left,right; - /* cleanse input a little */ - right = ((val >> 8) & 0xff) ; - left = (val & 0xff) ; - - if(right > 100) right = 100; - if(left > 100) left = 100; - - card->mix.mixer_state[mixer]=(right << 8) | left; - card->mix.write_mixer(card,mixer,left,right); -} - -static void -mixer_push_state(struct ess_card *card) -{ - int i; - for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { - if( ! supported_mixer(card,i)) continue; - - set_mixer(card,i,card->mix.mixer_state[i]); - } -} - -static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) -{ - int i, val=0; - unsigned long flags; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_CARD(card); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); - strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); - info.modify_counter = card->mix.modcnt; - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); - strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - - if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - if (_IOC_DIR(cmd) == _IOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* give them the current record source */ - - if(!card->mix.recmask_io) { - val = 0; - } else { - spin_lock_irqsave(&card->lock, flags); - val = card->mix.recmask_io(card,1,0); - spin_unlock_irqrestore(&card->lock, flags); - } - break; - - case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ - val = card->mix.supported_mixers; - break; - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - val = card->mix.record_sources; - break; - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - val = card->mix.stereo_mixers; - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: /* read a specific mixer */ - i = _IOC_NR(cmd); - - if ( ! supported_mixer(card,i)) - return -EINVAL; - - /* do we ever want to touch the hardware? */ -/* spin_lock_irqsave(&card->lock, flags); - val = card->mix.read_mixer(card,i); - spin_unlock_irqrestore(&card->lock, flags);*/ - - val = card->mix.mixer_state[i]; -/* M_printk("returned 0x%x for mixer %d\n",val,i);*/ - - break; - } - return put_user(val, p); - } - - if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) - return -EINVAL; - - card->mix.modcnt++; - - if (get_user(val, p)) - return -EFAULT; - - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - - if (!card->mix.recmask_io) return -EINVAL; - if(!val) return 0; - if(! (val &= card->mix.record_sources)) return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - card->mix.recmask_io(card,0,val); - spin_unlock_irqrestore(&card->lock, flags); - return 0; - - default: - i = _IOC_NR(cmd); - - if ( ! supported_mixer(card,i)) - return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - set_mixer(card,i,val); - spin_unlock_irqrestore(&card->lock, flags); - - return 0; - } -} - -/* --------------------------------------------------------------------- */ -static int ess_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct ess_card *card = NULL; - struct pci_dev *pdev = NULL; - struct pci_driver *drvr; - - while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { - drvr = pci_dev_driver (pdev); - if (drvr == &maestro_pci_driver) { - card = (struct ess_card*)pci_get_drvdata (pdev); - if (!card) - continue; - if (card->dev_mixer == minor) - break; - } - } - if (!card) - return -ENODEV; - file->private_data = card; - return nonseekable_open(inode, file); -} - -static int ess_release_mixdev(struct inode *inode, struct file *file) -{ - struct ess_card *card = (struct ess_card *)file->private_data; - - VALIDATE_CARD(card); - - return 0; -} - -static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct ess_card *card = (struct ess_card *)file->private_data; - - VALIDATE_CARD(card); - - return mixer_ioctl(card, cmd, arg); -} - -static /*const*/ struct file_operations ess_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = ess_ioctl_mixdev, - .open = ess_open_mixdev, - .release = ess_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct ess_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait,current); - unsigned long flags; - int count; - signed long tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - current->state = TASK_INTERRUPTIBLE; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - /* XXX uhm.. questionable locking*/ - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - tmo = (count * HZ) / s->ratedac; - tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; - /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. - or something. who cares. - zach */ - if (!schedule_timeout(tmo ? tmo : 1) && tmo) - M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ -/* Zach sez: "god this is gross.." */ -static int -comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, - int count, int bufsize) -{ - /* No such thing as stereo recording, so we - use dual input mixers. which means we have to - combine mono to stereo buffer. yuck. - - but we don't have to be able to work a byte at a time..*/ - - unsigned char *so,*left,*right; - int i; - - so = tmp_buffer; - left = real_buffer + offset; - right = real_buffer + bufsize/2 + offset; - -/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ - - for(i=count/4; i ; i--) { - (*(so+2)) = *(right++); - (*(so+3)) = *(right++); - (*so) = *(left++); - (*(so+1)) = *(left++); - so+=4; - } - - return 0; -} - -/* in this loop, dma_adc.count signifies the amount of data thats waiting - to be copied to the user's buffer. it is filled by the interrupt - handler and drained by this loop. */ -static ssize_t -ess_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - unsigned char *combbuf = NULL; - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if(!(combbuf = kmalloc(count,GFP_KERNEL))) - return -ENOMEM; - ret = 0; - - calc_bob_rate(s); - - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - /* remember, all these things are expressed in bytes to be - sent to the user.. hence the evil / 2 down below */ - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - spin_unlock_irqrestore(&s->lock, flags); - - if (cnt > count) - cnt = count; - - if ( cnt > 0 ) cnt &= ~3; - - if (cnt <= 0) { - start_adc(s); - if (file->f_flags & O_NONBLOCK) - { - ret = ret ? ret : -EAGAIN; - goto rec_return_free; - } - if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { - if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - stop_adc(s); - spin_lock_irqsave(&s->lock, flags); - set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); - /* program enhanced mode registers */ - /* FILL ME */ -/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); - wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) - { - ret = ret ? ret : -ERESTARTSYS; - goto rec_return_free; - } - continue; - } - - if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { - /* swptr/2 so that we know the real offset in each apu's buffer */ - comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); - if (copy_to_user(buffer, combbuf, cnt)) { - ret = ret ? ret : -EFAULT; - goto rec_return_free; - } - } else { - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - ret = ret ? ret : -EFAULT; - goto rec_return_free; - } - } - - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - start_adc(s); - } - -rec_return_free: - kfree(combbuf); - return ret; -} - -static ssize_t -ess_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - calc_bob_rate(s); - - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - - cnt = s->dma_dac.dmasize-swptr; - - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - - spin_unlock_irqrestore(&s->lock, flags); - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if(!ret) ret = -EAGAIN; - goto return_free; - } - if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { - if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - stop_dac(s); - spin_lock_irqsave(&s->lock, flags); - set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); - /* program enhanced mode registers */ -/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); - wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ - /* FILL ME */ - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - goto return_free; - } - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - goto return_free; - } -/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/ - - swptr = (swptr + cnt) % s->dma_dac.dmasize; - - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(s); - } -return_free: - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - -/* In 0.14 prog_dmabuf always returns success anyway ... */ - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf(s, 0)) - return 0; - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf(s, 1)) - return 0; - } - - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->dma_dac.wait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->dma_adc.wait, wait); - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int ess_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 1)) != 0) - goto out; - db = &s->dma_dac; - } else -#if 0 - /* if we can have the wp/wc do the combining - we can turn this back on. */ - if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 0)) != 0) - goto out; - db = &s->dma_adc; - } else -#endif - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EAGAIN; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - unsigned char fmtm, fmtd; - void __user *argp = (void __user *)arg; - int __user *p = argp; - -/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - /* XXX fix */ - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->card->pcidev->irq); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->card->pcidev->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - set_dac_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_U8|AFMT_S16_LE, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - /* fixed at 16bit for now */ - fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; -#if 0 - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); -#endif - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? - AFMT_S16_LE : - AFMT_U8, - p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) - val |= PCM_ENABLE_INPUT; - if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - start_adc(s); - } else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - start_dac(s); - } else - stop_dac(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - val = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - spin_lock_irqsave(&s->lock, flags); - ess_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) - return val; - return put_user(s->dma_dac.fragsize, p); - } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - M_printk("maestro: SETFRAGMENT: %0x\n",val); - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return -EINVAL; -} - -static void -set_base_registers(struct ess_state *s,void *vaddr) -{ - unsigned long packed_phys = virt_to_bus(vaddr)>>12; - wave_set_register(s, 0x01FC , packed_phys); - wave_set_register(s, 0x01FD , packed_phys); - wave_set_register(s, 0x01FE , packed_phys); - wave_set_register(s, 0x01FF , packed_phys); -} - -/* - * this guy makes sure we're in the right power - * state for what we want to be doing - */ -static void maestro_power(struct ess_card *card, int tostate) -{ - u16 active_mask = acpi_state_mask[tostate]; - u8 state; - - if(!use_pm) return; - - pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state); - state&=3; - - /* make sure we're in the right state */ - if(state != tostate) { - M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n", - card->pcidev->bus->number, - PCI_SLOT(card->pcidev->devfn), - PCI_FUNC(card->pcidev->devfn), - state,tostate); - pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate); - } - - /* and make sure the units we care about are on - XXX we might want to do this before state flipping? */ - pci_write_config_word(card->pcidev, 0x54, ~ active_mask); - pci_write_config_word(card->pcidev, 0x56, ~ active_mask); -} - -/* we allocate a large power of two for all our memory. - this is cut up into (not to scale :): - |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | -*/ -static int -allocate_buffers(struct ess_state *s) -{ - void *rawbuf=NULL; - int order,i; - struct page *page, *pend; - - /* alloc as big a chunk as we can */ - for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) - if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) - break; - - if (!rawbuf) - return 1; - - M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; - s->card->dmaorder = order; - - for(i=0;icard->channels[i]; - - if(ess->dev_audio == -1) - continue; - - ess->dma_dac.ready = s->dma_dac.mapped = 0; - ess->dma_adc.ready = s->dma_adc.mapped = 0; - ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; - - /* offset dac and adc buffers starting half way through and then at each [da][ad]c's - order's intervals.. */ - ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); - ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); - /* offset mixbuf by a mixbuf so that the lame status fifo can - happily scribble away.. */ - ess->mixbuf = rawbuf + (512 * (i+1)); - - M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf, - ess->dma_adc.rawbuf, ess->mixbuf); - - } - - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); - for (page = virt_to_page(rawbuf); page <= pend; page++) - SetPageReserved(page); - - return 0; -} -static void -free_buffers(struct ess_state *s) -{ - struct page *page, *pend; - - s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; - s->dma_dac.mapped = s->dma_adc.mapped = 0; - s->dma_dac.ready = s->dma_adc.ready = 0; - - M_printk("maestro: freeing %p\n",s->card->dmapages); - /* undo marking the pages as reserved */ - - pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); - for (page = virt_to_page(s->card->dmapages); page <= pend; page++) - ClearPageReserved(page); - - free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); - s->card->dmapages = NULL; -} - -static int -ess_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct ess_state *s = NULL; - unsigned char fmtm = ~0, fmts = 0; - struct pci_dev *pdev = NULL; - /* - * Scan the cards and find the channel. We only - * do this at open time so it is ok - */ - - while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { - struct ess_card *c; - struct pci_driver *drvr; - - drvr = pci_dev_driver (pdev); - if (drvr == &maestro_pci_driver) { - int i; - struct ess_state *sp; - - c = (struct ess_card*)pci_get_drvdata (pdev); - if (!c) - continue; - for(i=0;ichannels[i]; - if(sp->dev_audio < 0) - continue; - if((sp->dev_audio ^ minor) & ~0xf) - continue; - s=sp; - } - } - } - if (!s) - return -ENODEV; - - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EWOULDBLOCK; - } - mutex_unlock(&s->open_mutex); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - - /* under semaphore.. */ - if ((s->card->dmapages==NULL) && allocate_buffers(s)) { - mutex_unlock(&s->open_mutex); - return -ENOMEM; - } - - /* we're covered by the open_mutex */ - if( ! s->card->dsps_open ) { - maestro_power(s->card,ACPI_D0); - start_bob(s); - } - s->card->dsps_open++; - M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); - - /* ok, lets write WC base regs now that we've - powered up the chip */ - M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages), - ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12); - set_base_registers(s,s->card->dmapages); - - if (file->f_mode & FMODE_READ) { -/* - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ - - fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); - fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; - - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - set_dac_rate(s, 8000); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int -ess_release(struct inode *inode, struct file *file) -{ - struct ess_state *s = (struct ess_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_mutex); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - } - - s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - /* we're covered by the open_mutex */ - M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); - if( --s->card->dsps_open <= 0) { - s->card->dsps_open = 0; - stop_bob(s); - free_buffers(s); - maestro_power(s->card,ACPI_D2); - } - mutex_unlock(&s->open_mutex); - wake_up(&s->open_wait); - unlock_kernel(); - return 0; -} - -static struct file_operations ess_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = ess_read, - .write = ess_write, - .poll = ess_poll, - .ioctl = ess_ioctl, - .mmap = ess_mmap, - .open = ess_open, - .release = ess_release, -}; - -static int -maestro_config(struct ess_card *card) -{ - struct pci_dev *pcidev = card->pcidev; - struct ess_state *ess = &card->channels[0]; - int apu,iobase = card->iobase; - u16 w; - u32 n; - - /* We used to muck around with pci config space that - * we had no business messing with. We don't know enough - * about the machine to know which DMA mode is appropriate, - * etc. We were guessing wrong on some machines and making - * them unhappy. We now trust in the BIOS to do things right, - * which almost certainly means a new host of problems will - * arise with broken BIOS implementations. screw 'em. - * We're already intolerant of machines that don't assign - * IRQs. - */ - - /* do config work at full power */ - maestro_power(card,ACPI_D0); - - pci_read_config_word(pcidev, 0x50, &w); - - w&=~(1<<5); /* Don't swap left/right (undoc)*/ - - pci_write_config_word(pcidev, 0x50, w); - - pci_read_config_word(pcidev, 0x52, &w); - w&=~(1<<15); /* Turn off internal clock multiplier */ - /* XXX how do we know which to use? */ - w&=~(1<<14); /* External clock */ - - w|= (1<<7); /* Hardware volume control on */ - w|= (1<<6); /* Debounce off: easier to push the HWV buttons. */ - w&=~(1<<5); /* GPIO 4:5 */ - w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ - w&=~(1<<2); /* MIDI fix off (undoc) */ - w&=~(1<<1); /* reserved, always write 0 */ - pci_write_config_word(pcidev, 0x52, w); - - /* - * Legacy mode - */ - - pci_read_config_word(pcidev, 0x40, &w); - w|=(1<<15); /* legacy decode off */ - w&=~(1<<14); /* Disable SIRQ */ - w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ - - pci_write_config_word(pcidev, 0x40, w); - - /* Set up 978 docking control chip. */ - pci_read_config_word(pcidev, 0x58, &w); - w|=1<<2; /* Enable 978. */ - w|=1<<3; /* Turn on 978 hardware volume control. */ - w&=~(1<<11); /* Turn on 978 mixer volume control. */ - pci_write_config_word(pcidev, 0x58, w); - - sound_reset(iobase); - - /* - * Ring Bus Setup - */ - - /* setup usual 0x34 stuff.. 0x36 may be chip specific */ - outw(0xC090, iobase+0x34); /* direct sound, stereo */ - udelay(20); - outw(0x3000, iobase+0x36); /* direct sound, stereo */ - udelay(20); - - - /* - * Reset the CODEC - */ - - maestro_ac97_reset(iobase,pcidev); - - /* - * Ring Bus Setup - */ - - n=inl(iobase+0x34); - n&=~0xF000; - n|=12<<12; /* Direct Sound, Stereo */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x0F00; /* Modem off */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x00F0; - n|=9<<4; /* DAC, Stereo */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x000F; /* ASSP off */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n|=(1<<29); /* Enable ring bus */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n|=(1<<28); /* Enable serial bus */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x00F00000; /* MIC off */ - outl(n, iobase+0x34); - - n=inl(iobase+0x34); - n&=~0x000F0000; /* I2S off */ - outl(n, iobase+0x34); - - - w=inw(iobase+0x18); - w&=~(1<<7); /* ClkRun off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<6); /* Hardware volume control interrupt off... for now. */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<4); /* ASSP irq off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<3); /* ISDN irq off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w|=(1<<2); /* Direct Sound IRQ on */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w&=~(1<<1); /* MPU401 IRQ off */ - outw(w, iobase+0x18); - - w=inw(iobase+0x18); - w|=(1<<0); /* SB IRQ on */ - outw(w, iobase+0x18); - - /* Set hardware volume control registers to midpoints. - We can tell which button was pushed based on how they change. */ - outb(0x88, iobase+0x1c); - outb(0x88, iobase+0x1d); - outb(0x88, iobase+0x1e); - outb(0x88, iobase+0x1f); - - /* it appears some maestros (dell 7500) only work if these are set, - regardless of whether we use the assp or not. */ - - outb(0, iobase+0xA4); - outb(3, iobase+0xA2); - outb(0, iobase+0xA6); - - for(apu=0;apu<16;apu++) - { - /* Write 0 into the buffer area 0x1E0->1EF */ - outw(0x01E0+apu, 0x10+iobase); - outw(0x0000, 0x12+iobase); - - /* - * The 1.10 test program seem to write 0 into the buffer area - * 0x1D0-0x1DF too. - */ - outw(0x01D0+apu, 0x10+iobase); - outw(0x0000, 0x12+iobase); - } - -#if 1 - wave_set_register(ess, IDR7_WAVE_ROMRAM, - (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); - wave_set_register(ess, IDR7_WAVE_ROMRAM, - wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); - wave_set_register(ess, IDR7_WAVE_ROMRAM, - wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); - wave_set_register(ess, IDR7_WAVE_ROMRAM, - wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); -#else - maestro_write(ess, IDR7_WAVE_ROMRAM, - (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); - maestro_write(ess, IDR7_WAVE_ROMRAM, - maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); - maestro_write(ess, IDR7_WAVE_ROMRAM, - maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); - maestro_write(ess, IDR7_WAVE_ROMRAM, - maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); -#endif - - maestro_write(ess, IDR2_CRAM_DATA, 0x0000); - maestro_write(ess, 0x08, 0xB004); - /* Now back to the DirectSound stuff */ - maestro_write(ess, 0x09, 0x001B); - maestro_write(ess, 0x0A, 0x8000); - maestro_write(ess, 0x0B, 0x3F37); - maestro_write(ess, 0x0C, 0x0098); - - /* parallel out ?? */ - maestro_write(ess, 0x0C, - (maestro_read(ess, 0x0C)&~0xF000)|0x8000); - /* parallel in, has something to do with recording :) */ - maestro_write(ess, 0x0C, - (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); - - maestro_write(ess, 0x0D, 0x7632); - - /* Wave cache control on - test off, sg off, - enable, enable extra chans 1Mb */ - - outw(inw(0x14+iobase)|(1<<8),0x14+iobase); - outw(inw(0x14+iobase)&0xFE03,0x14+iobase); - outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); - outw(inw(0x14+iobase)|(1<<7),0x14+iobase); - - outw(0xA1A0, 0x14+iobase); /* 0300 ? */ - - /* Now clear the APU control ram */ - for(apu=0;apupower_regs = 0; - - /* check to see if we have a capabilities list in - the config register */ - pci_read_config_word(pcidev, PCI_STATUS, &w); - if(!(w & PCI_STATUS_CAP_LIST)) return 0; - - /* walk the list, starting at the head. */ - pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next); - - while(next && max--) { - pci_read_config_dword(pcidev, next & ~3, &n); - if((n & 0xff) == PCI_CAP_ID_PM) { - card->power_regs = next; - break; - } - next = ((n>>8) & 0xff); - } - - return card->power_regs ? 1 : 0; -} - -static int __init -maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid) -{ - int card_type = pdid->driver_data; - u32 n; - int iobase; - int i, ret; - struct ess_card *card; - struct ess_state *ess; - int num = 0; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - /* don't pick up weird modem maestros */ - if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) - return -ENODEV; - - - if ((ret=pci_enable_device(pcidev))) - return ret; - - iobase = pci_resource_start(pcidev,0); - if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO)) - return -ENODEV; - - if(pcidev->irq == 0) - return -ENODEV; - - /* stake our claim on the iospace */ - if( request_region(iobase, 256, card_names[card_type]) == NULL ) - { - printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); - return -EBUSY; - } - - /* just to be sure */ - pci_set_master(pcidev); - - card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); - if(card == NULL) - { - printk(KERN_WARNING "maestro: out of memory\n"); - release_region(iobase, 256); - return -ENOMEM; - } - - memset(card, 0, sizeof(*card)); - card->pcidev = pcidev; - - card->iobase = iobase; - card->card_type = card_type; - card->irq = pcidev->irq; - card->magic = ESS_CARD_MAGIC; - spin_lock_init(&card->lock); - init_waitqueue_head(&card->suspend_queue); - - card->dock_mute_vol = 50; - - /* init our groups of 6 apus */ - for(i=0;ichannels[i]; - - s->index = i; - - s->card = card; - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - spin_lock_init(&s->lock); - mutex_init(&s->open_mutex); - s->magic = ESS_STATE_MAGIC; - - s->apu[0] = 6*i; - s->apu[1] = (6*i)+1; - s->apu[2] = (6*i)+2; - s->apu[3] = (6*i)+3; - s->apu[4] = (6*i)+4; - s->apu[5] = (6*i)+5; - - if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) - printk("maestro: BOTCH!\n"); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) - break; - } - - num = i; - - /* clear the rest if we ran out of slots to register */ - for(;ichannels[i]; - s->dev_audio = -1; - } - - ess = &card->channels[0]; - - /* - * Ok card ready. Begin setup proper - */ - - printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", - card_names[card_type],iobase,card->irq); - pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); - printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); - - /* turn off power management unless: - * - the user explicitly asks for it - * or - * - we're not a 2e, lesser chipps seem to have problems. - * - we're not on our _very_ small whitelist. some implemenetations - * really don't like the pm code, others require it. - * feel free to expand this as required. - */ -#define SUBSYSTEM_VENDOR(x) (x&0xffff) - if( (use_pm != 1) && - ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028))) - use_pm = 0; - - if(!use_pm) - printk(KERN_INFO "maestro: not attempting power management.\n"); - else { - if(!parse_power(card,pcidev)) - printk(KERN_INFO "maestro: no PCI power management interface found.\n"); - else { - pci_read_config_dword(pcidev, card->power_regs, &n); - printk(KERN_INFO "maestro: PCI power management capability: 0x%x\n",n>>16); - } - } - - maestro_config(card); - - if(maestro_ac97_get(card, 0x00)==0x0080) { - printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" - "\tyou should tell someone about this.\n"); - } else { - maestro_ac97_init(card); - } - - if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { - printk("maestro: couldn't register mixer!\n"); - } else { - memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); - mixer_push_state(card); - } - - if((ret=request_irq(card->irq, ess_interrupt, IRQF_SHARED, card_names[card_type], card))) - { - printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); - unregister_sound_mixer(card->dev_mixer); - for(i=0;ichannels[i]; - if(s->dev_audio != -1) - unregister_sound_dsp(s->dev_audio); - } - release_region(card->iobase, 256); - unregister_reboot_notifier(&maestro_nb); - kfree(card); - return ret; - } - - /* Turn on hardware volume control interrupt. - This has to come after we grab the IRQ above, - or a crash will result on installation if a button has been pressed, - because in that case we'll get an immediate interrupt. */ - n = inw(iobase+0x18); - n|=(1<<6); - outw(n, iobase+0x18); - - pci_set_drvdata(pcidev,card); - /* now go to sleep 'till something interesting happens */ - maestro_power(card,ACPI_D2); - - printk(KERN_INFO "maestro: %d channels configured.\n", num); - return 0; -} - -static void maestro_remove(struct pci_dev *pcidev) { - struct ess_card *card = pci_get_drvdata(pcidev); - int i; - u32 n; - - /* XXX maybe should force stop bob, but should be all - stopped by _release by now */ - - /* Turn off hardware volume control interrupt. - This has to come before we leave the IRQ below, - or a crash results if a button is pressed ! */ - n = inw(card->iobase+0x18); - n&=~(1<<6); - outw(n, card->iobase+0x18); - - free_irq(card->irq, card); - unregister_sound_mixer(card->dev_mixer); - for(i=0;ichannels[i]; - if(ess->dev_audio != -1) - unregister_sound_dsp(ess->dev_audio); - } - /* Goodbye, Mr. Bond. */ - maestro_power(card,ACPI_D3); - release_region(card->iobase, 256); - kfree(card); - pci_set_drvdata(pcidev,NULL); -} - -static struct pci_device_id maestro_pci_tbl[] = { - {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2}, - {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E}, - {PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO}, - {0,} -}; -MODULE_DEVICE_TABLE(pci, maestro_pci_tbl); - -static struct pci_driver maestro_pci_driver = { - .name = "maestro", - .id_table = maestro_pci_tbl, - .probe = maestro_probe, - .remove = maestro_remove, -}; - -static int __init init_maestro(void) -{ - int rc; - - rc = pci_register_driver(&maestro_pci_driver); - if (rc < 0) - return rc; - - if (register_reboot_notifier(&maestro_nb)) - printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); -#ifdef MODULE - printk(version); -#endif - if (dsps_order < 0) { - dsps_order = 1; - printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); - } - else if (dsps_order > MAX_DSP_ORDER) { - dsps_order = MAX_DSP_ORDER; - printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); - } - return 0; -} - -static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) -{ - /* this notifier is called when the kernel is really shut down. */ - M_printk("maestro: shutting down\n"); - /* this will remove all card instances too */ - pci_unregister_driver(&maestro_pci_driver); - /* XXX dunno about power management */ - return NOTIFY_OK; -} - -/* --------------------------------------------------------------------- */ - - -static void cleanup_maestro(void) { - M_printk("maestro: unloading\n"); - pci_unregister_driver(&maestro_pci_driver); - unregister_reboot_notifier(&maestro_nb); -} - -/* --------------------------------------------------------------------- */ - -void -check_suspend(struct ess_card *card) -{ - DECLARE_WAITQUEUE(wait, current); - - if(!card->in_suspend) return; - - card->in_suspend++; - add_wait_queue(&(card->suspend_queue), &wait); - current->state = TASK_UNINTERRUPTIBLE; - schedule(); - remove_wait_queue(&(card->suspend_queue), &wait); - current->state = TASK_RUNNING; -} - -module_init(init_maestro); -module_exit(cleanup_maestro); diff --git a/sound/oss/maestro.h b/sound/oss/maestro.h deleted file mode 100644 index 023ec7f968..0000000000 --- a/sound/oss/maestro.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Registers for the ESS PCI cards - */ - -/* - * Memory access - */ - -#define ESS_MEM_DATA 0x00 -#define ESS_MEM_INDEX 0x02 - -/* - * AC-97 Codec port. Delay 1uS after each write. This is used to - * talk AC-97 (see intel.com). Write data then register. - */ - -#define ESS_AC97_INDEX 0x30 /* byte wide */ -#define ESS_AC97_DATA 0x32 - -/* - * Reading is a bit different. You write register|0x80 to ubdex - * delay 1uS poll the low bit of index, when it clears read the - * data value. - */ - -/* - * Control port. Not yet fully understood - * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 - * to the data port. Then after 4uS the value 0x300 is written - */ - -#define RING_BUS_CTRL_L 0x34 -#define RING_BUS_CTRL_H 0x36 - -/* - * This is also used during setup. The value 0x17 is written to it - */ - -#define ESS_SETUP_18 0x18 - -/* - * And this one gets 0x000b - */ - -#define ESS_SETUP_A2 0xA2 - -/* - * And this 0x0000 - */ - -#define ESS_SETUP_A4 0xA4 -#define ESS_SETUP_A6 0xA6 - -/* - * Stuff to do with Harpo - the wave stuff - */ - -#define ESS_WAVETABLE_SIZE 0x14 -#define ESS_WAVETABLE_2M 0xA180 - diff --git a/sound/oss/maestro3.c b/sound/oss/maestro3.c deleted file mode 100644 index 5548e3cff7..0000000000 --- a/sound/oss/maestro3.c +++ /dev/null @@ -1,2969 +0,0 @@ -/***************************************************************************** - * - * ESS Maestro3/Allegro driver for Linux 2.4.x - * - * 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. - * - * (c) Copyright 2000 Zach Brown - * - * I need to thank many people for helping make this driver happen. - * As always, Eric Brombaugh was a hacking machine and killed many bugs - * that I was too dumb to notice. Howard Kim at ESS provided reference boards - * and as much docs as he could. Todd and Mick at Dell tested snapshots on - * an army of laptops. msw and deviant at Red Hat also humoured me by hanging - * their laptops every few hours in the name of science. - * - * Shouts go out to Mike "DJ XPCom" Ang. - * - * History - * v1.23 - Jun 5 2002 - Michael Olson - * added a module option to allow selection of GPIO pin number - * for external amp - * v1.22 - Feb 28 2001 - Zach Brown - * allocate mem at insmod/setup, rather than open - * limit pci dma addresses to 28bit, thanks guys. - * v1.21 - Feb 04 2001 - Zach Brown - * fix up really dumb notifier -> suspend oops - * v1.20 - Jan 30 2001 - Zach Brown - * get rid of pm callback and use pci_dev suspend/resume instead - * m3_probe cleanups, including pm oops think-o - * v1.10 - Jan 6 2001 - Zach Brown - * revert to lame remap_page_range mmap() just to make it work - * record mmap fixed. - * fix up incredibly broken open/release resource management - * duh. fix record format setting. - * add SMP locking and cleanup formatting here and there - * v1.00 - Dec 16 2000 - Zach Brown - * port to sexy 2.4 interfaces - * properly align instance allocations so recording works - * clean up function namespace a little :/ - * update PCI IDs based on mail from ESS - * arbitrarily bump version number to show its 2.4 now, - * 2.2 will stay 0., oss_audio port gets 2. - * v0.03 - Nov 05 2000 - Zach Brown - * disable recording but allow dsp to be opened read - * pull out most silly compat defines - * v0.02 - Nov 04 2000 - Zach Brown - * changed clocking setup for m3, slowdown fixed. - * codec reset is hopefully reliable now - * rudimentary apm/power management makes suspend/resume work - * v0.01 - Oct 31 2000 - Zach Brown - * first release - * v0.00 - Sep 09 2000 - Zach Brown - * first pass derivation from maestro.c - * - * TODO - * in/out allocated contiguously so fullduplex mmap will work? - * no beep on init (mute) - * resetup msrc data memory if freq changes? - * - * -- - * - * Allow me to ramble a bit about the m3 architecture. The core of the - * chip is the 'assp', the custom ESS dsp that runs the show. It has - * a small amount of code and data ram. ESS drops binary dsp code images - * on our heads, but we don't get to see specs on the dsp. - * - * The constant piece of code on the dsp is the 'kernel'. It also has a - * chunk of the dsp memory that is statically set aside for its control - * info. This is the KDATA defines in maestro3.h. Part of its core - * data is a list of code addresses that point to the pieces of DSP code - * that it should walk through in its loop. These other pieces of code - * do the real work. The kernel presumably jumps into each of them in turn. - * These code images tend to have their own data area, and one can have - * multiple data areas representing different states for each of the 'client - * instance' code portions. There is generally a list in the kernel data - * that points to the data instances for a given piece of code. - * - * We've only been given the binary image for the 'minisrc', mini sample - * rate converter. This is rather annoying because it limits the work - * we can do on the dsp, but it also greatly simplifies the job of managing - * dsp data memory for the code and data for our playing streams :). We - * statically allocate the minisrc code into a region we 'know' to be free - * based on the map of the binary kernel image we're loading. We also - * statically allocate the data areas for the maximum number of pcm streams - * we can be dealing with. This max is set by the length of the static list - * in the kernel data that records the number of minisrc data regions we - * can have. Thats right, all software dsp mixing with static code list - * limits. Rock. - * - * How sound goes in and out is still a relative mystery. It appears - * that the dsp has the ability to get input and output through various - * 'connections'. To do IO from or to a connection, you put the address - * of the minisrc client area in the static kernel data lists for that - * input or output. so for pcm -> dsp -> mixer, we put the minisrc data - * instance in the DMA list and also in the list for the mixer. I guess - * it Just Knows which is in/out, and we give some dma control info that - * helps. There are all sorts of cool inputs/outputs that it seems we can't - * use without dsp code images that know how to use them. - * - * So at init time we preload all the memory allocation stuff and set some - * system wide parameters. When we really get a sound to play we build - * up its minisrc header (stream parameters, buffer addresses, input/output - * settings). Then we throw its header on the various lists. We also - * tickle some KDATA settings that ask the assp to raise clock interrupts - * and do some amount of software mixing before handing data to the ac97. - * - * Sorry for the vague details. Feel free to ask Eric or myself if you - * happen to be trying to use this driver elsewhere. Please accept my - * apologies for the quality of the OSS support code, its passed through - * too many hands now and desperately wants to be rethought. - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - -#include "maestro3.h" - -#define M_DEBUG 1 - -#define DRIVER_VERSION "1.23" -#define M3_MODULE_NAME "maestro3" -#define PFX M3_MODULE_NAME ": " - -#define M3_STATE_MAGIC 0x734d724d -#define M3_CARD_MAGIC 0x646e6f50 - -#define ESS_FMT_STEREO 0x01 -#define ESS_FMT_16BIT 0x02 -#define ESS_FMT_MASK 0x03 -#define ESS_DAC_SHIFT 0 -#define ESS_ADC_SHIFT 4 - -#define DAC_RUNNING 1 -#define ADC_RUNNING 2 - -#define SND_DEV_DSP16 5 - -#ifdef M_DEBUG -static int debug; -#define DPMOD 1 /* per module load */ -#define DPSTR 2 /* per 'stream' */ -#define DPSYS 3 /* per syscall */ -#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */ -#define DPINT 5 /* per interrupt, LOTS */ -#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);} -#else -#define DPRINTK(x) -#endif - -struct m3_list { - int curlen; - u16 mem_addr; - int max; -}; - -static int external_amp = 1; -static int gpio_pin = -1; - -struct m3_state { - unsigned int magic; - struct m3_card *card; - unsigned char fmt, enable; - - int index; - - /* this locks around the oss state in the driver */ - /* no, this lock is removed - only use card->lock */ - /* otherwise: against what are you protecting on SMP - when irqhandler uses s->lock - and m3_assp_read uses card->lock ? - */ - struct mutex open_mutex; - wait_queue_head_t open_wait; - mode_t open_mode; - - int dev_audio; - - struct assp_instance { - u16 code, data; - } dac_inst, adc_inst; - - /* should be in dmabuf */ - unsigned int rateadc, ratedac; - - struct dmabuf { - void *rawbuf; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned endcleared:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - /* new in m3 */ - int mixer_index, dma_index, msrc_index, adc1_index; - int in_lists; - /* 2.4.. */ - dma_addr_t handle; - - } dma_dac, dma_adc; -}; - -struct m3_card { - unsigned int magic; - - struct m3_card *next; - - struct ac97_codec *ac97; - spinlock_t ac97_lock; - - int card_type; - -#define NR_DSPS 1 -#define MAX_DSPS NR_DSPS - struct m3_state channels[MAX_DSPS]; - - /* this locks around the physical registers on the card */ - spinlock_t lock; - - /* hardware resources */ - struct pci_dev *pcidev; - u32 iobase; - u32 irq; - - int dacs_active; - - int timer_users; - - struct m3_list msrc_list, - mixer_list, - adc1_list, - dma_list; - - /* for storing reset state..*/ - u8 reset_state; - - u16 *suspend_mem; - int in_suspend; - wait_queue_head_t suspend_queue; -}; - -/* - * an arbitrary volume we set the internal - * volume settings to so that the ac97 volume - * range is a little less insane. 0x7fff is - * max. - */ -#define ARB_VOLUME ( 0x6800 ) - -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -enum { - ESS_ALLEGRO, - ESS_MAESTRO3, - /* - * a maestro3 with 'hardware strapping', only - * found inside ESS? - */ - ESS_MAESTRO3HW, -}; - -static char *card_names[] = { - [ESS_ALLEGRO] = "Allegro", - [ESS_MAESTRO3] = "Maestro3(i)", - [ESS_MAESTRO3HW] = "Maestro3(i)hw" -}; - -#ifndef PCI_VENDOR_ESS -#define PCI_VENDOR_ESS 0x125D -#endif - -#define M3_DEVICE(DEV, TYPE) \ -{ \ -.vendor = PCI_VENDOR_ESS, \ -.device = DEV, \ -.subvendor = PCI_ANY_ID, \ -.subdevice = PCI_ANY_ID, \ -.class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, \ -.class_mask = 0xffff << 8, \ -.driver_data = TYPE, \ -} - -static struct pci_device_id m3_id_table[] = { - M3_DEVICE(0x1988, ESS_ALLEGRO), - M3_DEVICE(0x1998, ESS_MAESTRO3), - M3_DEVICE(0x199a, ESS_MAESTRO3HW), - {0,} -}; - -MODULE_DEVICE_TABLE (pci, m3_id_table); - -/* - * reports seem to indicate that the m3 is limited - * to 28bit bus addresses. aaaargggh... - */ -#define M3_PCI_DMA_MASK 0x0fffffff - -static unsigned -ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -static struct m3_card *devs; - -/* - * I'm not very good at laying out functions in a file :) - */ -static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf); -static int m3_suspend(struct pci_dev *pci_dev, pm_message_t state); -static void check_suspend(struct m3_card *card); - -static struct notifier_block m3_reboot_nb = { - .notifier_call = m3_notifier, -}; - -static void m3_outw(struct m3_card *card, - u16 value, unsigned long reg) -{ - check_suspend(card); - outw(value, card->iobase + reg); -} - -static u16 m3_inw(struct m3_card *card, unsigned long reg) -{ - check_suspend(card); - return inw(card->iobase + reg); -} -static void m3_outb(struct m3_card *card, - u8 value, unsigned long reg) -{ - check_suspend(card); - outb(value, card->iobase + reg); -} -static u8 m3_inb(struct m3_card *card, unsigned long reg) -{ - check_suspend(card); - return inb(card->iobase + reg); -} - -/* - * access 16bit words to the code or data regions of the dsp's memory. - * index addresses 16bit words. - */ -static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index) -{ - m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); - m3_outw(card, index, DSP_PORT_MEMORY_INDEX); - return m3_inw(card, DSP_PORT_MEMORY_DATA); -} -static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index) -{ - unsigned long flags; - u16 ret; - - spin_lock_irqsave(&(card->lock), flags); - ret = __m3_assp_read(card, region, index); - spin_unlock_irqrestore(&(card->lock), flags); - - return ret; -} - -static void __m3_assp_write(struct m3_card *card, - u16 region, u16 index, u16 data) -{ - m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); - m3_outw(card, index, DSP_PORT_MEMORY_INDEX); - m3_outw(card, data, DSP_PORT_MEMORY_DATA); -} -static void m3_assp_write(struct m3_card *card, - u16 region, u16 index, u16 data) -{ - unsigned long flags; - - spin_lock_irqsave(&(card->lock), flags); - __m3_assp_write(card, region, index, data); - spin_unlock_irqrestore(&(card->lock), flags); -} - -static void m3_assp_halt(struct m3_card *card) -{ - card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; - mdelay(10); - m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); -} - -static void m3_assp_continue(struct m3_card *card) -{ - m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); -} - -/* - * This makes me sad. the maestro3 has lists - * internally that must be packed.. 0 terminates, - * apparently, or maybe all unused entries have - * to be 0, the lists have static lengths set - * by the binary code images. - */ - -static int m3_add_list(struct m3_card *card, - struct m3_list *list, u16 val) -{ - DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n", - val, list, list->curlen); - - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + list->curlen, - val); - - return list->curlen++; - -} - -static void m3_remove_list(struct m3_card *card, - struct m3_list *list, int index) -{ - u16 val; - int lastindex = list->curlen - 1; - - DPRINTK(DPSTR, "removing ind %d from list 0x%p\n", - index, list); - - if(index != lastindex) { - val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + lastindex); - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + index, - val); - } - - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - list->mem_addr + lastindex, - 0); - - list->curlen--; -} - -static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data) -{ - int tmp; - - s->fmt = (s->fmt & mask) | data; - - tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; - - /* write to 'mono' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, - (tmp & ESS_FMT_STEREO) ? 0 : 1); - /* write to '8bit' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, - (tmp & ESS_FMT_16BIT) ? 0 : 1); - - tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK; - - /* write to 'mono' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, - (tmp & ESS_FMT_STEREO) ? 0 : 1); - /* write to '8bit' word */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, - (tmp & ESS_FMT_16BIT) ? 0 : 1); -} - -static void set_dac_rate(struct m3_state *s, unsigned int rate) -{ - u32 freq; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - - s->ratedac = rate; - - freq = ((rate << 15) + 24000 ) / 48000; - if(freq) - freq--; - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_FREQUENCY, - freq); -} - -static void set_adc_rate(struct m3_state *s, unsigned int rate) -{ - u32 freq; - - if (rate > 48000) - rate = 48000; - if (rate < 8000) - rate = 8000; - - s->rateadc = rate; - - freq = ((rate << 15) + 24000 ) / 48000; - if(freq) - freq--; - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_FREQUENCY, - freq); -} - -static void inc_timer_users(struct m3_card *card) -{ - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - card->timer_users++; - DPRINTK(DPSYS, "inc timer users now %d\n", - card->timer_users); - if(card->timer_users != 1) - goto out; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_RELOAD, - 240 ) ; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_CURRENT, - 240 ) ; - - m3_outw(card, - m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, - HOST_INT_CTRL); -out: - spin_unlock_irqrestore(&card->lock, flags); -} - -static void dec_timer_users(struct m3_card *card) -{ - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - - card->timer_users--; - DPRINTK(DPSYS, "dec timer users now %d\n", - card->timer_users); - if(card->timer_users > 0 ) - goto out; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_RELOAD, - 0 ) ; - - __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TIMER_COUNT_CURRENT, - 0 ) ; - - m3_outw(card, m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, - HOST_INT_CTRL); -out: - spin_unlock_irqrestore(&card->lock, flags); -} - -/* - * {start,stop}_{adc,dac} should be called - * while holding the 'state' lock and they - * will try to grab the 'card' lock.. - */ -static void stop_adc(struct m3_state *s) -{ - if (! (s->enable & ADC_RUNNING)) - return; - - s->enable &= ~ADC_RUNNING; - dec_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_INSTANCE_READY, 0); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_ADC1_REQUEST, 0); -} - -static void stop_dac(struct m3_state *s) -{ - if (! (s->enable & DAC_RUNNING)) - return; - - DPRINTK(DPSYS, "stop_dac()\n"); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_INSTANCE_READY, 0); - - s->enable &= ~DAC_RUNNING; - s->card->dacs_active--; - dec_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_MIXER_TASK_NUMBER, - s->card->dacs_active ) ; -} - -static void start_dac(struct m3_state *s) -{ - if( (!s->dma_dac.mapped && s->dma_dac.count < 1) || - !s->dma_dac.ready || - (s->enable & DAC_RUNNING)) - return; - - DPRINTK(DPSYS, "start_dac()\n"); - - s->enable |= DAC_RUNNING; - s->card->dacs_active++; - inc_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_INSTANCE_READY, 1); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_MIXER_TASK_NUMBER, - s->card->dacs_active ) ; -} - -static void start_adc(struct m3_state *s) -{ - if ((! s->dma_adc.mapped && - s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - || !s->dma_adc.ready - || (s->enable & ADC_RUNNING) ) - return; - - DPRINTK(DPSYS, "start_adc()\n"); - - s->enable |= ADC_RUNNING; - inc_timer_users(s->card); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - KDATA_ADC1_REQUEST, 1); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_INSTANCE_READY, 1); -} - -static struct play_vals { - u16 addr, val; -} pv[] = { - {CDATA_LEFT_VOLUME, ARB_VOLUME}, - {CDATA_RIGHT_VOLUME, ARB_VOLUME}, - {SRC3_DIRECTION_OFFSET, 0} , - /* +1, +2 are stereo/16 bit */ - {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ - {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ - {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ - {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ - {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ - {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ - {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ - {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ - {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ - {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ - {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ - {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ - {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ - {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ - {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ - {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ - {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ -}; - - -/* the mode passed should be already shifted and masked */ -static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) -{ - int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); - int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); - int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); - int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; - struct dmabuf *db = &s->dma_dac; - int i; - - DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n", - mode, rate, buffer, size); - -#define LO(x) ((x) & 0xffff) -#define HI(x) LO((x) >> 16) - - /* host dma buffer pointers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_ADDRL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_ADDRH, - HI(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L, - LO(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H, - HI(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_CURRENTL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_HOST_SRC_CURRENTH, - HI(virt_to_bus(buffer))); -#undef LO -#undef HI - - /* dsp buffers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_BEGIN, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1, - dsp_in_buffer + (dsp_in_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_HEAD, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_IN_BUF_TAIL, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_BEGIN, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1, - dsp_out_buffer + (dsp_out_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_HEAD, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_OUT_BUF_TAIL, - dsp_out_buffer); - - /* - * some per client initializers - */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12, - s->dac_inst.data + 40 + 8); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19, - s->dac_inst.code + MINISRC_COEF_LOC); - - /* enable or disable low pass filter? */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22, - s->ratedac > 45000 ? 0xff : 0 ); - - /* tell it which way dma is going? */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + CDATA_DMA_CONTROL, - DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); - - /* - * set an armload of static initializers - */ - for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->dac_inst.data + pv[i].addr, pv[i].val); - - /* - * put us in the lists if we're not already there - */ - - if(db->in_lists == 0) { - - db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, - s->dac_inst.data >> DP_SHIFT_COUNT); - - db->dma_index = m3_add_list(s->card, &s->card->dma_list, - s->dac_inst.data >> DP_SHIFT_COUNT); - - db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, - s->dac_inst.data >> DP_SHIFT_COUNT); - - db->in_lists = 1; - } - - set_dac_rate(s,rate); - start_dac(s); -} - -/* - * Native record driver - */ -static struct rec_vals { - u16 addr, val; -} rv[] = { - {CDATA_LEFT_VOLUME, ARB_VOLUME}, - {CDATA_RIGHT_VOLUME, ARB_VOLUME}, - {SRC3_DIRECTION_OFFSET, 1} , - /* +1, +2 are stereo/16 bit */ - {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ - {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ - {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ - {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ - {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ - {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ - {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ - {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ - {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ - {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ - {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ - {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ - {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ - {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ - {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ - {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ - {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ - {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ - {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ -}; - -/* again, passed mode is alrady shifted/masked */ -static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) -{ - int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); - int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); - int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); - int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; - struct dmabuf *db = &s->dma_adc; - int i; - - DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n", - mode, rate, buffer, size); - -#define LO(x) ((x) & 0xffff) -#define HI(x) LO((x) >> 16) - - /* host dma buffer pointers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_ADDRL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_ADDRH, - HI(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L, - LO(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H, - HI(virt_to_bus(buffer) + size)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_CURRENTL, - LO(virt_to_bus(buffer))); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_HOST_SRC_CURRENTH, - HI(virt_to_bus(buffer))); -#undef LO -#undef HI - - /* dsp buffers */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_BEGIN, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1, - dsp_in_buffer + (dsp_in_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_HEAD, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_IN_BUF_TAIL, - dsp_in_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_BEGIN, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1, - dsp_out_buffer + (dsp_out_size / 2)); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_HEAD, - dsp_out_buffer); - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_OUT_BUF_TAIL, - dsp_out_buffer); - - /* - * some per client initializers - */ - - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12, - s->adc_inst.data + 40 + 8); - - /* tell it which way dma is going? */ - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + CDATA_DMA_CONTROL, - DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + - DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); - - /* - * set an armload of static initializers - */ - for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) - m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, - s->adc_inst.data + rv[i].addr, rv[i].val); - - /* - * put us in the lists if we're not already there - */ - - if(db->in_lists == 0) { - - db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, - s->adc_inst.data >> DP_SHIFT_COUNT); - - db->dma_index = m3_add_list(s->card, &s->card->dma_list, - s->adc_inst.data >> DP_SHIFT_COUNT); - - db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, - s->adc_inst.data >> DP_SHIFT_COUNT); - - db->in_lists = 1; - } - - set_adc_rate(s,rate); - start_adc(s); -} -/* --------------------------------------------------------------------- */ - -static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count) -{ - DPRINTK(DPINT,"set_dmaa??\n"); -} - -static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count) -{ - DPRINTK(DPINT,"set_dmac??\n"); -} - -static u32 get_dma_pos(struct m3_card *card, - int instance_addr) -{ - u16 hi = 0, lo = 0; - int retry = 10; - - /* - * try and get a valid answer - */ - while(retry--) { - hi = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - instance_addr + CDATA_HOST_SRC_CURRENTH); - - lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - instance_addr + CDATA_HOST_SRC_CURRENTL); - - if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA, - instance_addr + CDATA_HOST_SRC_CURRENTH)) - break; - } - return lo | (hi<<16); -} - -static u32 get_dmaa(struct m3_state *s) -{ - u32 offset; - - offset = get_dma_pos(s->card, s->dac_inst.data) - - virt_to_bus(s->dma_dac.rawbuf); - - DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset); - - return offset; -} - -static u32 get_dmac(struct m3_state *s) -{ - u32 offset; - - offset = get_dma_pos(s->card, s->adc_inst.data) - - virt_to_bus(s->dma_adc.rawbuf); - - DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset); - - return offset; - -} - -static int -prog_dmabuf(struct m3_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - unsigned bytepersec; - unsigned bufs; - unsigned char fmt; - unsigned long flags; - - spin_lock_irqsave(&s->card->lock, flags); - - fmt = s->fmt; - if (rec) { - stop_adc(s); - fmt >>= ESS_ADC_SHIFT; - } else { - stop_dac(s); - fmt >>= ESS_DAC_SHIFT; - } - fmt &= ESS_FMT_MASK; - - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - - DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); - - memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); - - if (rec) - m3_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); - else - m3_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); - - db->ready = 1; - - spin_unlock_irqrestore(&s->card->lock, flags); - - return 0; -} - -static void clear_advance(struct m3_state *s) -{ - unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; - - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - /* account for wrapping? */ - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); -} - -/* call with spinlock held! */ -static void m3_update_ptr(struct m3_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - hwptr = get_dmac(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - stop_adc(s); - /* brute force everyone back in sync, sigh */ - s->dma_adc.count = 0; - s->dma_adc.swptr = 0; - s->dma_adc.hwptr = 0; - s->dma_adc.error++; - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmaa(s) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - - DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n", - hwptr,diff,s->dma_dac.count); - - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - - if (s->dma_dac.mapped) { - - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { - wake_up(&s->dma_dac.wait); - } - } else { - - s->dma_dac.count -= diff; - - if (s->dma_dac.count <= 0) { - DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", - diff, diff, - s->dma_dac.count, - s->dma_dac.count, - hwptr, hwptr, - s->dma_dac.swptr, - s->dma_dac.swptr); - stop_dac(s); - /* brute force everyone back in sync, sigh */ - s->dma_dac.count = 0; - s->dma_dac.swptr = hwptr; - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { - wake_up(&s->dma_dac.wait); - DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n", - s->dma_dac.count, s->dma_dac.swptr, hwptr); - } - } - } -} - -static irqreturn_t m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct m3_card *c = (struct m3_card *)dev_id; - struct m3_state *s = &c->channels[0]; - u8 status; - - status = inb(c->iobase+0x1A); - - if(status == 0xff) - return IRQ_NONE; - - /* presumably acking the ints? */ - outw(status, c->iobase+0x1A); - - if(c->in_suspend) - return IRQ_HANDLED; - - /* - * ack an assp int if its running - * and has an int pending - */ - if( status & ASSP_INT_PENDING) { - u8 ctl = inb(c->iobase + ASSP_CONTROL_B); - if( !(ctl & STOP_ASSP_CLOCK)) { - ctl = inb(c->iobase + ASSP_HOST_INT_STATUS ); - if(ctl & DSP2HOST_REQ_TIMER) { - outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS); - /* update adc/dac info if it was a timer int */ - spin_lock(&c->lock); - m3_update_ptr(s); - spin_unlock(&c->lock); - } - } - } - - /* XXX is this needed? */ - if(status & 0x40) - outb(0x40, c->iobase+0x1A); - return IRQ_HANDLED; -} - - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n"; - -#define VALIDATE_MAGIC(FOO,MAG) \ -({ \ - if (!(FOO) || (FOO)->magic != MAG) { \ - printk(invalid_magic,__FUNCTION__); \ - return -ENXIO; \ - } \ -}) - -#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC) -#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC) - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct m3_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait,current); - unsigned long flags; - int count; - signed long tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - spin_lock_irqsave(&s->card->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->card->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = (count * HZ) / s->ratedac; - tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; - /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. - or something. who cares. - zach */ - if (!schedule_timeout(tmo ? tmo : 1) && tmo) - DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static ssize_t m3_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; - - spin_lock_irqsave(&s->card->lock, flags); - - while (count > 0) { - int timed_out; - - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - start_adc(s); - if (file->f_flags & O_NONBLOCK) - { - ret = ret ? ret : -EAGAIN; - goto out; - } - - spin_unlock_irqrestore(&s->card->lock, flags); - timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0; - spin_lock_irqsave(&s->card->lock, flags); - - if(timed_out) { - printk("read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - stop_adc(s); - set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - } - if (signal_pending(current)) - { - ret = ret ? ret : -ERESTARTSYS; - goto out; - } - continue; - } - - spin_unlock_irqrestore(&s->card->lock, flags); - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - ret = ret ? ret : -EFAULT; - return ret; - } - spin_lock_irqsave(&s->card->lock, flags); - - swptr = (swptr + cnt) % s->dma_adc.dmasize; - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - count -= cnt; - buffer += cnt; - ret += cnt; - start_adc(s); - } - -out: - spin_unlock_irqrestore(&s->card->lock, flags); - return ret; -} - -static ssize_t m3_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - - spin_lock_irqsave(&s->card->lock, flags); - - while (count > 0) { - int timed_out; - - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - - cnt = s->dma_dac.dmasize-swptr; - - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if(!ret) ret = -EAGAIN; - goto out; - } - spin_unlock_irqrestore(&s->card->lock, flags); - timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0; - spin_lock_irqsave(&s->card->lock, flags); - if(timed_out) { - DPRINTK(DPCRAP,"write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - stop_dac(s); - set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - } - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - goto out; - } - continue; - } - spin_unlock_irqrestore(&s->card->lock, flags); - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - return ret; - } - spin_lock_irqsave(&s->card->lock, flags); - - DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n", - cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr); - - swptr = (swptr + cnt) % s->dma_dac.dmasize; - - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(s); - } -out: - spin_unlock_irqrestore(&s->card->lock, flags); - return ret; -} - -static unsigned int m3_poll(struct file *file, struct poll_table_struct *wait) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->dma_dac.wait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->dma_adc.wait, wait); - - spin_lock_irqsave(&s->card->lock, flags); - m3_update_ptr(s); - - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - - spin_unlock_irqrestore(&s->card->lock, flags); - return mask; -} - -static int m3_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - unsigned long max_size, size, start, offset; - struct dmabuf *db; - int ret = -EINVAL; - - VALIDATE_STATE(s); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 0)) != 0) - return ret; - db = &s->dma_dac; - } else - if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 1)) != 0) - return ret; - db = &s->dma_adc; - } else - return -EINVAL; - - max_size = db->dmasize; - - start = vma->vm_start; - offset = (vma->vm_pgoff << PAGE_SHIFT); - size = vma->vm_end - vma->vm_start; - - if(size > max_size) - goto out; - if(offset > max_size - size) - goto out; - - /* - * this will be ->nopage() once I can - * ask Jeff what the hell I'm doing wrong. - */ - ret = -EAGAIN; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - - db->mapped = 1; - ret = 0; - -out: - return ret; -} - -/* - * this function is a disaster.. - */ -#define get_user_ret(x, ptr, ret) ({ if(get_user(x, ptr)) return ret; }) -static int m3_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - struct m3_card *card=s->card; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - unsigned char fmtm, fmtd; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - - DPRINTK(DPSYS,"m3_ioctl: cmd %d\n", cmd); - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - /* XXX fix */ - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: - spin_lock_irqsave(&card->lock, flags); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->card->pcidev->irq); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->card->pcidev->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - spin_unlock_irqrestore(&card->lock, flags); - return 0; - - case SNDCTL_DSP_SPEED: - get_user_ret(val, p, -EFAULT); - spin_lock_irqsave(&card->lock, flags); - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - set_dac_rate(s, val); - } - } - spin_unlock_irqrestore(&card->lock, flags); - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SNDCTL_DSP_STEREO: - get_user_ret(val, p, -EFAULT); - spin_lock_irqsave(&card->lock, flags); - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - spin_unlock_irqrestore(&card->lock, flags); - return 0; - - case SNDCTL_DSP_CHANNELS: - get_user_ret(val, p, -EFAULT); - spin_lock_irqsave(&card->lock, flags); - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - spin_unlock_irqrestore(&card->lock, flags); - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_U8|AFMT_S16_LE, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - get_user_ret(val, p, -EFAULT); - spin_lock_irqsave(&card->lock, flags); - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - else - fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); - } - set_fmt(s, fmtm, fmtd); - } - spin_unlock_irqrestore(&card->lock, flags); - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? - AFMT_S16_LE : - AFMT_U8, - p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) - val |= PCM_ENABLE_INPUT; - if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - get_user_ret(val, p, -EFAULT); - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - start_adc(s); - } else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - start_dac(s); - } else - stop_dac(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&card->lock, flags); - m3_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&card->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!(s->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&card->lock, flags); - m3_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&card->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&card->lock, flags); - m3_update_ptr(s); - val = s->dma_dac.count; - spin_unlock_irqrestore(&card->lock, flags); - return put_user(val, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&card->lock, flags); - m3_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&card->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&card->lock, flags); - m3_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&card->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) - return val; - return put_user(s->dma_dac.fragsize, p); - } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - get_user_ret(val, p, -EFAULT); - spin_lock_irqsave(&card->lock, flags); - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - } - spin_unlock_irqrestore(&card->lock, flags); - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - get_user_ret(val, p, -EFAULT); - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) - : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) - : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return -EINVAL; -} - -static int -allocate_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) -{ - int order; - - DPRINTK(DPSTR,"allocating for dmabuf %p\n", db); - - /* - * alloc as big a chunk as we can, start with - * 64k 'cause we're insane. based on order cause - * the amazingly complicated prog_dmabuf wants it. - * - * pci_alloc_sonsistent guarantees that it won't cross a natural - * boundary; the m3 hardware can't have dma cross a 64k bus - * address boundary. - */ - for (order = 16-PAGE_SHIFT; order >= 1; order--) { - db->rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order, - &(db->handle)); - if(db->rawbuf) - break; - } - - if (!db->rawbuf) - return 1; - - DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n", - PAGE_SIZE<rawbuf); - - { - struct page *page, *pend; - - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << order) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - - - db->buforder = order; - db->ready = 0; - db->mapped = 0; - - return 0; -} - -static void -nuke_lists(struct m3_card *card, struct dmabuf *db) -{ - m3_remove_list(card, &(card->dma_list), db->dma_index); - m3_remove_list(card, &(card->msrc_list), db->msrc_index); - db->in_lists = 0; -} - -static void -free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) -{ - if(db->rawbuf == NULL) - return; - - DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db); - - { - struct page *page, *pend; - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - } - - - pci_free_consistent(pci_dev, PAGE_SIZE << db->buforder, - db->rawbuf, db->handle); - - db->rawbuf = NULL; - db->buforder = 0; - db->mapped = 0; - db->ready = 0; -} - -static int m3_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct m3_card *c; - struct m3_state *s = NULL; - int i; - unsigned char fmtm = ~0, fmts = 0; - unsigned long flags; - - /* - * Scan the cards and find the channel. We only - * do this at open time so it is ok - */ - for(c = devs ; c != NULL ; c = c->next) { - - for(i=0;ichannels[i].dev_audio < 0) - continue; - if((c->channels[i].dev_audio ^ minor) & ~0xf) - continue; - - s = &c->channels[i]; - break; - } - } - - if (!s) - return -ENODEV; - - VALIDATE_STATE(s); - - file->private_data = s; - - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EWOULDBLOCK; - } - mutex_unlock(&s->open_mutex); - interruptible_sleep_on(&s->open_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - - spin_lock_irqsave(&c->lock, flags); - - if (file->f_mode & FMODE_READ) { - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; - - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; - - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - set_dac_rate(s, 8000); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - - mutex_unlock(&s->open_mutex); - spin_unlock_irqrestore(&c->lock, flags); - return nonseekable_open(inode, file); -} - -static int m3_release(struct inode *inode, struct file *file) -{ - struct m3_state *s = (struct m3_state *)file->private_data; - struct m3_card *card=s->card; - unsigned long flags; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - - mutex_lock(&s->open_mutex); - spin_lock_irqsave(&card->lock, flags); - - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - if(s->dma_dac.in_lists) { - m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index); - nuke_lists(s->card, &(s->dma_dac)); - } - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - if(s->dma_adc.in_lists) { - m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index); - nuke_lists(s->card, &(s->dma_adc)); - } - } - - s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - - spin_unlock_irqrestore(&card->lock, flags); - mutex_unlock(&s->open_mutex); - wake_up(&s->open_wait); - - return 0; -} - -/* - * Wait for the ac97 serial bus to be free. - * return nonzero if the bus is still busy. - */ -static int m3_ac97_wait(struct m3_card *card) -{ - int i = 10000; - - while( (m3_inb(card, 0x30) & 1) && i--) ; - - return i == 0; -} - -static u16 m3_ac97_read(struct ac97_codec *codec, u8 reg) -{ - u16 ret = 0; - struct m3_card *card = codec->private_data; - - spin_lock(&card->ac97_lock); - - if(m3_ac97_wait(card)) { - printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg); - goto out; - } - - m3_outb(card, 0x80 | (reg & 0x7f), 0x30); - - if(m3_ac97_wait(card)) { - printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg); - goto out; - } - - ret = m3_inw(card, 0x32); - DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg); - -out: - spin_unlock(&card->ac97_lock); - return ret; -} - -static void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) -{ - struct m3_card *card = codec->private_data; - - spin_lock(&card->ac97_lock); - - if(m3_ac97_wait(card)) { - printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg); - goto out; - } - DPRINTK(DPCRAP,"writing 0x%04x to 0x%02x\n", val, reg); - - m3_outw(card, val, 0x32); - m3_outb(card, reg & 0x7f, 0x30); -out: - spin_unlock(&card->ac97_lock); -} -/* OSS /dev/mixer file operation methods */ -static int m3_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct m3_card *card = devs; - - for (card = devs; card != NULL; card = card->next) { - if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor)) - break; - } - - if (!card) { - return -ENODEV; - } - - file->private_data = card->ac97; - - return nonseekable_open(inode, file); -} - -static int m3_release_mixdev(struct inode *inode, struct file *file) -{ - return 0; -} - -static int m3_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct ac97_codec *codec = (struct ac97_codec *)file->private_data; - - return codec->mixer_ioctl(codec, cmd, arg); -} - -static struct file_operations m3_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = m3_ioctl_mixdev, - .open = m3_open_mixdev, - .release = m3_release_mixdev, -}; - -static void remote_codec_config(int io, int isremote) -{ - isremote = isremote ? 1 : 0; - - outw( (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, - io + RING_BUS_CTRL_B); - outw( (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, - io + SDO_OUT_DEST_CTRL); - outw( (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, - io + SDO_IN_DEST_CTRL); -} - -/* - * hack, returns non zero on err - */ -static int try_read_vendor(struct m3_card *card) -{ - u16 ret; - - if(m3_ac97_wait(card)) - return 1; - - m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); - - if(m3_ac97_wait(card)) - return 1; - - ret = m3_inw(card, 0x32); - - return (ret == 0) || (ret == 0xffff); -} - -static void m3_codec_reset(struct m3_card *card, int busywait) -{ - u16 dir; - int delay1 = 0, delay2 = 0, i; - int io = card->iobase; - - switch (card->card_type) { - /* - * the onboard codec on the allegro seems - * to want to wait a very long time before - * coming back to life - */ - case ESS_ALLEGRO: - delay1 = 50; - delay2 = 800; - break; - case ESS_MAESTRO3: - case ESS_MAESTRO3HW: - delay1 = 20; - delay2 = 500; - break; - } - - for(i = 0; i < 5; i ++) { - dir = inw(io + GPIO_DIRECTION); - dir |= 0x10; /* assuming pci bus master? */ - - remote_codec_config(io, 0); - - outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); - udelay(20); - - outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); - outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); - outw(0, io + GPIO_DATA); - outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); - - if(busywait) { - mdelay(delay1); - } else { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((delay1 * HZ) / 1000); - } - - outw(GPO_PRIMARY_AC97, io + GPIO_DATA); - udelay(5); - /* ok, bring back the ac-link */ - outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); - outw(~0, io + GPIO_MASK); - - if(busywait) { - mdelay(delay2); - } else { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((delay2 * HZ) / 1000); - } - if(! try_read_vendor(card)) - break; - - delay1 += 10; - delay2 += 100; - - DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n", - delay1, delay2); - } - -#if 0 - /* more gung-ho reset that doesn't - * seem to work anywhere :) - */ - tmp = inw(io + RING_BUS_CTRL_A); - outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); - mdelay(20); - outw(tmp, io + RING_BUS_CTRL_A); - mdelay(50); -#endif -} - -static int __devinit m3_codec_install(struct m3_card *card) -{ - struct ac97_codec *codec; - - if ((codec = ac97_alloc_codec()) == NULL) - return -ENOMEM; - - codec->private_data = card; - codec->codec_read = m3_ac97_read; - codec->codec_write = m3_ac97_write; - /* someday we should support secondary codecs.. */ - codec->id = 0; - - if (ac97_probe_codec(codec) == 0) { - printk(KERN_ERR PFX "codec probe failed\n"); - ac97_release_codec(codec); - return -1; - } - - if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) { - printk(KERN_ERR PFX "couldn't register mixer!\n"); - ac97_release_codec(codec); - return -1; - } - - card->ac97 = codec; - - return 0; -} - - -#define MINISRC_LPF_LEN 10 -static u16 minisrc_lpf[MINISRC_LPF_LEN] = { - 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, - 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F -}; -static void m3_assp_init(struct m3_card *card) -{ - int i; - - /* zero kernel data */ - for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_BASE_ADDR + i, 0); - - /* zero mixer data? */ - for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_BASE_ADDR2 + i, 0); - - /* init dma pointer */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_CURRENT_DMA, - KDATA_DMA_XFER0); - - /* write kernel into code memory.. */ - for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - REV_B_CODE_MEMORY_BEGIN + i, - assp_kernel_image[i]); - } - - /* - * We only have this one client and we know that 0x400 - * is free in our kernel's mem map, so lets just - * drop it there. It seems that the minisrc doesn't - * need vectors, so we won't bother with them.. - */ - for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) { - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - 0x400 + i, - assp_minisrc_image[i]); - } - - /* - * write the coefficients for the low pass filter? - */ - for(i = 0; i < MINISRC_LPF_LEN ; i++) { - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - 0x400 + MINISRC_COEF_LOC + i, - minisrc_lpf[i]); - } - - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, - 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, - 0x8000); - - /* - * the minisrc is the only thing on - * our task list.. - */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_TASK0, - 0x400); - - /* - * init the mixer number.. - */ - - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_MIXER_TASK_NUMBER,0); - - /* - * EXTREME KERNEL MASTER VOLUME - */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); - - card->mixer_list.mem_addr = KDATA_MIXER_XFER0; - card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; - card->adc1_list.mem_addr = KDATA_ADC1_XFER0; - card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; - card->dma_list.mem_addr = KDATA_DMA_XFER0; - card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; - card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; - card->msrc_list.max = MAX_INSTANCE_MINISRC; -} - -static int setup_msrc(struct m3_card *card, - struct assp_instance *inst, int index) -{ - int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + - MINISRC_IN_BUFFER_SIZE / 2 + - 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); - int address, i; - - /* - * the revb memory map has 0x1100 through 0x1c00 - * free. - */ - - /* - * align instance address to 256 bytes so that it's - * shifted list address is aligned. - * list address = (mem address >> 1) >> 7; - */ - data_bytes = (data_bytes + 255) & ~255; - address = 0x1100 + ((data_bytes/2) * index); - - if((address + (data_bytes/2)) >= 0x1c00) { - printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n", - data_bytes, index, address); - return -1; - } - - for(i = 0; i < data_bytes/2 ; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - address + i, 0); - - inst->code = 0x400; - inst->data = address; - - return 0; -} - -static int m3_assp_client_init(struct m3_state *s) -{ - setup_msrc(s->card, &(s->dac_inst), s->index * 2); - setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1); - - return 0; -} - -static void m3_amp_enable(struct m3_card *card, int enable) -{ - /* - * this works for the reference board, have to find - * out about others - * - * this needs more magic for 4 speaker, but.. - */ - int io = card->iobase; - u16 gpo, polarity_port, polarity; - - if(!external_amp) - return; - - if (gpio_pin >= 0 && gpio_pin <= 15) { - polarity_port = 0x1000 + (0x100 * gpio_pin); - } else { - switch (card->card_type) { - case ESS_ALLEGRO: - polarity_port = 0x1800; - break; - default: - polarity_port = 0x1100; - /* Panasonic toughbook CF72 has to be different... */ - if(card->pcidev->subsystem_vendor == 0x10F7 && card->pcidev->subsystem_device == 0x833D) - polarity_port = 0x1D00; - break; - } - } - - gpo = (polarity_port >> 8) & 0x0F; - polarity = polarity_port >> 12; - if ( enable ) - polarity = !polarity; - polarity = polarity << gpo; - gpo = 1 << gpo; - - outw(~gpo , io + GPIO_MASK); - - outw( inw(io + GPIO_DIRECTION) | gpo , - io + GPIO_DIRECTION); - - outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) , - io + GPIO_DATA); - - outw(0xffff , io + GPIO_MASK); -} - -static int -maestro_config(struct m3_card *card) -{ - struct pci_dev *pcidev = card->pcidev; - u32 n; - u8 t; /* makes as much sense as 'n', no? */ - - pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); - n &= REDUCED_DEBOUNCE; - n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; - pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); - - outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B); - pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); - n &= ~INT_CLK_SELECT; - if(card->card_type >= ESS_MAESTRO3) { - n &= ~INT_CLK_MULT_ENABLE; - n |= INT_CLK_SRC_NOT_PCI; - } - n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); - pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); - - if(card->card_type <= ESS_ALLEGRO) { - pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); - n |= IN_CLK_12MHZ_SELECT; - pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); - } - - t = inb(card->iobase + ASSP_CONTROL_A); - t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); - t |= ASSP_CLK_49MHZ_SELECT; - t |= ASSP_0_WS_ENABLE; - outb(t, card->iobase + ASSP_CONTROL_A); - - outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); - - return 0; -} - -static void m3_enable_ints(struct m3_card *card) -{ - unsigned long io = card->iobase; - - outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); - outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, - io + ASSP_CONTROL_C); -} - -static struct file_operations m3_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = m3_read, - .write = m3_write, - .poll = m3_poll, - .ioctl = m3_ioctl, - .mmap = m3_mmap, - .open = m3_open, - .release = m3_release, -}; - -#ifdef CONFIG_PM -static int alloc_dsp_suspendmem(struct m3_card *card) -{ - int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); - - if( (card->suspend_mem = vmalloc(len)) == NULL) - return 1; - - return 0; -} - -#else -#define alloc_dsp_suspendmem(args...) 0 -#endif - -/* - * great day! this function is ugly as hell. - */ -static int __devinit m3_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) -{ - u32 n; - int i; - struct m3_card *card = NULL; - int ret = 0; - int card_type = pci_id->driver_data; - - DPRINTK(DPMOD, "in maestro_install\n"); - - if (pci_enable_device(pci_dev)) - return -EIO; - - if (pci_set_dma_mask(pci_dev, M3_PCI_DMA_MASK)) { - printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n"); - return -ENODEV; - } - - pci_set_master(pci_dev); - - if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING PFX "out of memory\n"); - return -ENOMEM; - } - memset(card, 0, sizeof(struct m3_card)); - card->pcidev = pci_dev; - init_waitqueue_head(&card->suspend_queue); - - if ( ! request_region(pci_resource_start(pci_dev, 0), - pci_resource_len (pci_dev, 0), M3_MODULE_NAME)) { - - printk(KERN_WARNING PFX "unable to reserve I/O space.\n"); - ret = -EBUSY; - goto out; - } - - card->iobase = pci_resource_start(pci_dev, 0); - - if(alloc_dsp_suspendmem(card)) { - printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n", - REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); - ret = -ENOMEM; - goto out; - } - - card->card_type = card_type; - card->irq = pci_dev->irq; - card->next = devs; - card->magic = M3_CARD_MAGIC; - spin_lock_init(&card->lock); - spin_lock_init(&card->ac97_lock); - devs = card; - for(i = 0; ichannels[i]); - s->dev_audio = -1; - } - - printk(KERN_INFO PFX "Configuring ESS %s found at IO 0x%04X IRQ %d\n", - card_names[card->card_type], card->iobase, card->irq); - - pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &n); - printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); - - maestro_config(card); - m3_assp_halt(card); - - m3_codec_reset(card, 0); - - if(m3_codec_install(card)) { - ret = -EIO; - goto out; - } - - m3_assp_init(card); - m3_amp_enable(card, 1); - - for(i=0;ichannels[i]; - - s->index = i; - - s->card = card; - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - mutex_init(&(s->open_mutex)); - s->magic = M3_STATE_MAGIC; - - m3_assp_client_init(s); - - if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) - printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n"); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) { - break; - } - - if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) || - allocate_dmabuf(card->pcidev, &(s->dma_dac))) { - ret = -ENOMEM; - goto out; - } - } - - if(request_irq(card->irq, m3_interrupt, IRQF_SHARED, card_names[card->card_type], card)) { - - printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq); - - ret = -EIO; - goto out; - } - - pci_set_drvdata(pci_dev, card); - - m3_enable_ints(card); - m3_assp_continue(card); - -out: - if(ret) { - if(card->iobase) - release_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); - vfree(card->suspend_mem); - if(card->ac97) { - unregister_sound_mixer(card->ac97->dev_mixer); - kfree(card->ac97); - } - for(i=0;ichannels[i]; - if(s->dev_audio != -1) - unregister_sound_dsp(s->dev_audio); - } - kfree(card); - } - - return ret; -} - -static void m3_remove(struct pci_dev *pci_dev) -{ - struct m3_card *card; - - unregister_reboot_notifier(&m3_reboot_nb); - - while ((card = devs)) { - int i; - devs = devs->next; - - free_irq(card->irq, card); - unregister_sound_mixer(card->ac97->dev_mixer); - kfree(card->ac97); - - for(i=0;ichannels[i]; - if(s->dev_audio < 0) - continue; - - unregister_sound_dsp(s->dev_audio); - free_dmabuf(card->pcidev, &s->dma_adc); - free_dmabuf(card->pcidev, &s->dma_dac); - } - - release_region(card->iobase, 256); - vfree(card->suspend_mem); - kfree(card); - } - devs = NULL; -} - -/* - * some bioses like the sound chip to be powered down - * at shutdown. We're just calling _suspend to - * achieve that.. - */ -static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf) -{ - struct m3_card *card; - - DPRINTK(DPMOD, "notifier suspending all cards\n"); - - for(card = devs; card != NULL; card = card->next) { - if(!card->in_suspend) - m3_suspend(card->pcidev, PMSG_SUSPEND); /* XXX legal? */ - } - return 0; -} - -static int m3_suspend(struct pci_dev *pci_dev, pm_message_t state) -{ - unsigned long flags; - int i; - struct m3_card *card = pci_get_drvdata(pci_dev); - - /* must be a better way.. */ - spin_lock_irqsave(&card->lock, flags); - - DPRINTK(DPMOD, "pm in dev %p\n",card); - - for(i=0;ichannels[i]; - - if(s->dev_audio == -1) - continue; - - DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i); - stop_dac(s); - stop_adc(s); - } - - mdelay(10); /* give the assp a chance to idle.. */ - - m3_assp_halt(card); - - if(card->suspend_mem) { - int index = 0; - - DPRINTK(DPMOD, "saving code\n"); - for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) - card->suspend_mem[index++] = - m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i); - DPRINTK(DPMOD, "saving data\n"); - for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - card->suspend_mem[index++] = - m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i); - } - - DPRINTK(DPMOD, "powering down apci regs\n"); - m3_outw(card, 0xffff, 0x54); - m3_outw(card, 0xffff, 0x56); - - card->in_suspend = 1; - - spin_unlock_irqrestore(&card->lock, flags); - - return 0; -} - -static int m3_resume(struct pci_dev *pci_dev) -{ - unsigned long flags; - int index; - int i; - struct m3_card *card = pci_get_drvdata(pci_dev); - - spin_lock_irqsave(&card->lock, flags); - card->in_suspend = 0; - - DPRINTK(DPMOD, "resuming\n"); - - /* first lets just bring everything back. .*/ - - DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card); - m3_outw(card, 0, 0x54); - m3_outw(card, 0, 0x56); - - DPRINTK(DPMOD, "restoring pci configs and reseting codec\n"); - maestro_config(card); - m3_assp_halt(card); - m3_codec_reset(card, 1); - - DPRINTK(DPMOD, "restoring dsp code card\n"); - index = 0; - for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, - card->suspend_mem[index++]); - for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, - card->suspend_mem[index++]); - - /* tell the dma engine to restart itself */ - m3_assp_write(card, MEMTYPE_INTERNAL_DATA, - KDATA_DMA_ACTIVE, 0); - - DPRINTK(DPMOD, "resuming dsp\n"); - m3_assp_continue(card); - - DPRINTK(DPMOD, "enabling ints\n"); - m3_enable_ints(card); - - /* bring back the old school flavor */ - for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) { - int state = card->ac97->mixer_state[i]; - if (!supported_mixer(card->ac97, i)) - continue; - - card->ac97->write_mixer(card->ac97, i, - state & 0xff, (state >> 8) & 0xff); - } - - m3_amp_enable(card, 1); - - /* - * now we flip on the music - */ - for(i=0;ichannels[i]; - if(s->dev_audio == -1) - continue; - /* - * db->ready makes it so these guys can be - * called unconditionally.. - */ - DPRINTK(DPMOD, "turning on dacs ind %d\n",i); - start_dac(s); - start_adc(s); - } - - spin_unlock_irqrestore(&card->lock, flags); - - /* - * all right, we think things are ready, - * wake up people who were using the device - * when we suspended - */ - wake_up(&card->suspend_queue); - - return 0; -} - -MODULE_AUTHOR("Zach Brown "); -MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver"); -MODULE_LICENSE("GPL"); - -#ifdef M_DEBUG -module_param(debug, int, 0); -#endif -module_param(external_amp, int, 0); -module_param(gpio_pin, int, 0); - -static struct pci_driver m3_pci_driver = { - .name = "ess_m3_audio", - .id_table = m3_id_table, - .probe = m3_probe, - .remove = m3_remove, - .suspend = m3_suspend, - .resume = m3_resume, -}; - -static int __init m3_init_module(void) -{ - printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n"); - - if (register_reboot_notifier(&m3_reboot_nb)) { - printk(KERN_WARNING PFX "reboot notifier registration failed\n"); - return -ENODEV; /* ? */ - } - - if (pci_register_driver(&m3_pci_driver)) { - unregister_reboot_notifier(&m3_reboot_nb); - return -ENODEV; - } - return 0; -} - -static void __exit m3_cleanup_module(void) -{ - pci_unregister_driver(&m3_pci_driver); -} - -module_init(m3_init_module); -module_exit(m3_cleanup_module); - -void check_suspend(struct m3_card *card) -{ - DECLARE_WAITQUEUE(wait, current); - - if(!card->in_suspend) - return; - - card->in_suspend++; - add_wait_queue(&card->suspend_queue, &wait); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - remove_wait_queue(&card->suspend_queue, &wait); - set_current_state(TASK_RUNNING); -} diff --git a/sound/oss/maestro3.h b/sound/oss/maestro3.h deleted file mode 100644 index dde29862c5..0000000000 --- a/sound/oss/maestro3.h +++ /dev/null @@ -1,821 +0,0 @@ -/* - * ESS Technology allegro audio driver. - * - * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.com) - * - * 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. - * - * Hacked for the maestro3 driver by zab - */ - -// Allegro PCI configuration registers -#define PCI_LEGACY_AUDIO_CTRL 0x40 -#define SOUND_BLASTER_ENABLE 0x00000001 -#define FM_SYNTHESIS_ENABLE 0x00000002 -#define GAME_PORT_ENABLE 0x00000004 -#define MPU401_IO_ENABLE 0x00000008 -#define MPU401_IRQ_ENABLE 0x00000010 -#define ALIAS_10BIT_IO 0x00000020 -#define SB_DMA_MASK 0x000000C0 -#define SB_DMA_0 0x00000040 -#define SB_DMA_1 0x00000040 -#define SB_DMA_R 0x00000080 -#define SB_DMA_3 0x000000C0 -#define SB_IRQ_MASK 0x00000700 -#define SB_IRQ_5 0x00000000 -#define SB_IRQ_7 0x00000100 -#define SB_IRQ_9 0x00000200 -#define SB_IRQ_10 0x00000300 -#define MIDI_IRQ_MASK 0x00003800 -#define SERIAL_IRQ_ENABLE 0x00004000 -#define DISABLE_LEGACY 0x00008000 - -#define PCI_ALLEGRO_CONFIG 0x50 -#define SB_ADDR_240 0x00000004 -#define MPU_ADDR_MASK 0x00000018 -#define MPU_ADDR_330 0x00000000 -#define MPU_ADDR_300 0x00000008 -#define MPU_ADDR_320 0x00000010 -#define MPU_ADDR_340 0x00000018 -#define USE_PCI_TIMING 0x00000040 -#define POSTED_WRITE_ENABLE 0x00000080 -#define DMA_POLICY_MASK 0x00000700 -#define DMA_DDMA 0x00000000 -#define DMA_TDMA 0x00000100 -#define DMA_PCPCI 0x00000200 -#define DMA_WBDMA16 0x00000400 -#define DMA_WBDMA4 0x00000500 -#define DMA_WBDMA2 0x00000600 -#define DMA_WBDMA1 0x00000700 -#define DMA_SAFE_GUARD 0x00000800 -#define HI_PERF_GP_ENABLE 0x00001000 -#define PIC_SNOOP_MODE_0 0x00002000 -#define PIC_SNOOP_MODE_1 0x00004000 -#define SOUNDBLASTER_IRQ_MASK 0x00008000 -#define RING_IN_ENABLE 0x00010000 -#define SPDIF_TEST_MODE 0x00020000 -#define CLK_MULT_MODE_SELECT_2 0x00040000 -#define EEPROM_WRITE_ENABLE 0x00080000 -#define CODEC_DIR_IN 0x00100000 -#define HV_BUTTON_FROM_GD 0x00200000 -#define REDUCED_DEBOUNCE 0x00400000 -#define HV_CTRL_ENABLE 0x00800000 -#define SPDIF_ENABLE 0x01000000 -#define CLK_DIV_SELECT 0x06000000 -#define CLK_DIV_BY_48 0x00000000 -#define CLK_DIV_BY_49 0x02000000 -#define CLK_DIV_BY_50 0x04000000 -#define CLK_DIV_RESERVED 0x06000000 -#define PM_CTRL_ENABLE 0x08000000 -#define CLK_MULT_MODE_SELECT 0x30000000 -#define CLK_MULT_MODE_SHIFT 28 -#define CLK_MULT_MODE_0 0x00000000 -#define CLK_MULT_MODE_1 0x10000000 -#define CLK_MULT_MODE_2 0x20000000 -#define CLK_MULT_MODE_3 0x30000000 -#define INT_CLK_SELECT 0x40000000 -#define INT_CLK_MULT_RESET 0x80000000 - -// M3 -#define INT_CLK_SRC_NOT_PCI 0x00100000 -#define INT_CLK_MULT_ENABLE 0x80000000 - -#define PCI_ACPI_CONTROL 0x54 -#define PCI_ACPI_D0 0x00000000 -#define PCI_ACPI_D1 0xB4F70000 -#define PCI_ACPI_D2 0xB4F7B4F7 - -#define PCI_USER_CONFIG 0x58 -#define EXT_PCI_MASTER_ENABLE 0x00000001 -#define SPDIF_OUT_SELECT 0x00000002 -#define TEST_PIN_DIR_CTRL 0x00000004 -#define AC97_CODEC_TEST 0x00000020 -#define TRI_STATE_BUFFER 0x00000080 -#define IN_CLK_12MHZ_SELECT 0x00000100 -#define MULTI_FUNC_DISABLE 0x00000200 -#define EXT_MASTER_PAIR_SEL 0x00000400 -#define PCI_MASTER_SUPPORT 0x00000800 -#define STOP_CLOCK_ENABLE 0x00001000 -#define EAPD_DRIVE_ENABLE 0x00002000 -#define REQ_TRI_STATE_ENABLE 0x00004000 -#define REQ_LOW_ENABLE 0x00008000 -#define MIDI_1_ENABLE 0x00010000 -#define MIDI_2_ENABLE 0x00020000 -#define SB_AUDIO_SYNC 0x00040000 -#define HV_CTRL_TEST 0x00100000 -#define SOUNDBLASTER_TEST 0x00400000 - -#define PCI_USER_CONFIG_C 0x5C - -#define PCI_DDMA_CTRL 0x60 -#define DDMA_ENABLE 0x00000001 - - -// Allegro registers -#define HOST_INT_CTRL 0x18 -#define SB_INT_ENABLE 0x0001 -#define MPU401_INT_ENABLE 0x0002 -#define ASSP_INT_ENABLE 0x0010 -#define RING_INT_ENABLE 0x0020 -#define HV_INT_ENABLE 0x0040 -#define CLKRUN_GEN_ENABLE 0x0100 -#define HV_CTRL_TO_PME 0x0400 -#define SOFTWARE_RESET_ENABLE 0x8000 - -/* - * should be using the above defines, probably. - */ -#define REGB_ENABLE_RESET 0x01 -#define REGB_STOP_CLOCK 0x10 - -#define HOST_INT_STATUS 0x1A -#define SB_INT_PENDING 0x01 -#define MPU401_INT_PENDING 0x02 -#define ASSP_INT_PENDING 0x10 -#define RING_INT_PENDING 0x20 -#define HV_INT_PENDING 0x40 - -#define HARDWARE_VOL_CTRL 0x1B -#define SHADOW_MIX_REG_VOICE 0x1C -#define HW_VOL_COUNTER_VOICE 0x1D -#define SHADOW_MIX_REG_MASTER 0x1E -#define HW_VOL_COUNTER_MASTER 0x1F - -#define CODEC_COMMAND 0x30 -#define CODEC_READ_B 0x80 - -#define CODEC_STATUS 0x30 -#define CODEC_BUSY_B 0x01 - -#define CODEC_DATA 0x32 - -#define RING_BUS_CTRL_A 0x36 -#define RAC_PME_ENABLE 0x0100 -#define RAC_SDFS_ENABLE 0x0200 -#define LAC_PME_ENABLE 0x0400 -#define LAC_SDFS_ENABLE 0x0800 -#define SERIAL_AC_LINK_ENABLE 0x1000 -#define IO_SRAM_ENABLE 0x2000 -#define IIS_INPUT_ENABLE 0x8000 - -#define RING_BUS_CTRL_B 0x38 -#define SECOND_CODEC_ID_MASK 0x0003 -#define SPDIF_FUNC_ENABLE 0x0010 -#define SECOND_AC_ENABLE 0x0020 -#define SB_MODULE_INTF_ENABLE 0x0040 -#define SSPE_ENABLE 0x0040 -#define M3I_DOCK_ENABLE 0x0080 - -#define SDO_OUT_DEST_CTRL 0x3A -#define COMMAND_ADDR_OUT 0x0003 -#define PCM_LR_OUT_LOCAL 0x0000 -#define PCM_LR_OUT_REMOTE 0x0004 -#define PCM_LR_OUT_MUTE 0x0008 -#define PCM_LR_OUT_BOTH 0x000C -#define LINE1_DAC_OUT_LOCAL 0x0000 -#define LINE1_DAC_OUT_REMOTE 0x0010 -#define LINE1_DAC_OUT_MUTE 0x0020 -#define LINE1_DAC_OUT_BOTH 0x0030 -#define PCM_CLS_OUT_LOCAL 0x0000 -#define PCM_CLS_OUT_REMOTE 0x0040 -#define PCM_CLS_OUT_MUTE 0x0080 -#define PCM_CLS_OUT_BOTH 0x00C0 -#define PCM_RLF_OUT_LOCAL 0x0000 -#define PCM_RLF_OUT_REMOTE 0x0100 -#define PCM_RLF_OUT_MUTE 0x0200 -#define PCM_RLF_OUT_BOTH 0x0300 -#define LINE2_DAC_OUT_LOCAL 0x0000 -#define LINE2_DAC_OUT_REMOTE 0x0400 -#define LINE2_DAC_OUT_MUTE 0x0800 -#define LINE2_DAC_OUT_BOTH 0x0C00 -#define HANDSET_OUT_LOCAL 0x0000 -#define HANDSET_OUT_REMOTE 0x1000 -#define HANDSET_OUT_MUTE 0x2000 -#define HANDSET_OUT_BOTH 0x3000 -#define IO_CTRL_OUT_LOCAL 0x0000 -#define IO_CTRL_OUT_REMOTE 0x4000 -#define IO_CTRL_OUT_MUTE 0x8000 -#define IO_CTRL_OUT_BOTH 0xC000 - -#define SDO_IN_DEST_CTRL 0x3C -#define STATUS_ADDR_IN 0x0003 -#define PCM_LR_IN_LOCAL 0x0000 -#define PCM_LR_IN_REMOTE 0x0004 -#define PCM_LR_RESERVED 0x0008 -#define PCM_LR_IN_BOTH 0x000C -#define LINE1_ADC_IN_LOCAL 0x0000 -#define LINE1_ADC_IN_REMOTE 0x0010 -#define LINE1_ADC_IN_MUTE 0x0020 -#define MIC_ADC_IN_LOCAL 0x0000 -#define MIC_ADC_IN_REMOTE 0x0040 -#define MIC_ADC_IN_MUTE 0x0080 -#define LINE2_DAC_IN_LOCAL 0x0000 -#define LINE2_DAC_IN_REMOTE 0x0400 -#define LINE2_DAC_IN_MUTE 0x0800 -#define HANDSET_IN_LOCAL 0x0000 -#define HANDSET_IN_REMOTE 0x1000 -#define HANDSET_IN_MUTE 0x2000 -#define IO_STATUS_IN_LOCAL 0x0000 -#define IO_STATUS_IN_REMOTE 0x4000 - -#define SPDIF_IN_CTRL 0x3E -#define SPDIF_IN_ENABLE 0x0001 - -#define GPIO_DATA 0x60 -#define GPIO_DATA_MASK 0x0FFF -#define GPIO_HV_STATUS 0x3000 -#define GPIO_PME_STATUS 0x4000 - -#define GPIO_MASK 0x64 -#define GPIO_DIRECTION 0x68 -#define GPO_PRIMARY_AC97 0x0001 -#define GPI_LINEOUT_SENSE 0x0004 -#define GPO_SECONDARY_AC97 0x0008 -#define GPI_VOL_DOWN 0x0010 -#define GPI_VOL_UP 0x0020 -#define GPI_IIS_CLK 0x0040 -#define GPI_IIS_LRCLK 0x0080 -#define GPI_IIS_DATA 0x0100 -#define GPI_DOCKING_STATUS 0x0100 -#define GPI_HEADPHONE_SENSE 0x0200 -#define GPO_EXT_AMP_SHUTDOWN 0x1000 - -// M3 -#define GPO_M3_EXT_AMP_SHUTDN 0x0002 - -#define ASSP_INDEX_PORT 0x80 -#define ASSP_MEMORY_PORT 0x82 -#define ASSP_DATA_PORT 0x84 - -#define MPU401_DATA_PORT 0x98 -#define MPU401_STATUS_PORT 0x99 - -#define CLK_MULT_DATA_PORT 0x9C - -#define ASSP_CONTROL_A 0xA2 -#define ASSP_0_WS_ENABLE 0x01 -#define ASSP_CTRL_A_RESERVED1 0x02 -#define ASSP_CTRL_A_RESERVED2 0x04 -#define ASSP_CLK_49MHZ_SELECT 0x08 -#define FAST_PLU_ENABLE 0x10 -#define ASSP_CTRL_A_RESERVED3 0x20 -#define DSP_CLK_36MHZ_SELECT 0x40 - -#define ASSP_CONTROL_B 0xA4 -#define RESET_ASSP 0x00 -#define RUN_ASSP 0x01 -#define ENABLE_ASSP_CLOCK 0x00 -#define STOP_ASSP_CLOCK 0x10 -#define RESET_TOGGLE 0x40 - -#define ASSP_CONTROL_C 0xA6 -#define ASSP_HOST_INT_ENABLE 0x01 -#define FM_ADDR_REMAP_DISABLE 0x02 -#define HOST_WRITE_PORT_ENABLE 0x08 - -#define ASSP_HOST_INT_STATUS 0xAC -#define DSP2HOST_REQ_PIORECORD 0x01 -#define DSP2HOST_REQ_I2SRATE 0x02 -#define DSP2HOST_REQ_TIMER 0x04 - -// AC97 registers -// XXX fix this crap up -/*#define AC97_RESET 0x00*/ - -#define AC97_VOL_MUTE_B 0x8000 -#define AC97_VOL_M 0x1F -#define AC97_LEFT_VOL_S 8 - -#define AC97_MASTER_VOL 0x02 -#define AC97_LINE_LEVEL_VOL 0x04 -#define AC97_MASTER_MONO_VOL 0x06 -#define AC97_PC_BEEP_VOL 0x0A -#define AC97_PC_BEEP_VOL_M 0x0F -#define AC97_SROUND_MASTER_VOL 0x38 -#define AC97_PC_BEEP_VOL_S 1 - -/*#define AC97_PHONE_VOL 0x0C -#define AC97_MIC_VOL 0x0E*/ -#define AC97_MIC_20DB_ENABLE 0x40 - -/*#define AC97_LINEIN_VOL 0x10 -#define AC97_CD_VOL 0x12 -#define AC97_VIDEO_VOL 0x14 -#define AC97_AUX_VOL 0x16*/ -#define AC97_PCM_OUT_VOL 0x18 -/*#define AC97_RECORD_SELECT 0x1A*/ -#define AC97_RECORD_MIC 0x00 -#define AC97_RECORD_CD 0x01 -#define AC97_RECORD_VIDEO 0x02 -#define AC97_RECORD_AUX 0x03 -#define AC97_RECORD_MONO_MUX 0x02 -#define AC97_RECORD_DIGITAL 0x03 -#define AC97_RECORD_LINE 0x04 -#define AC97_RECORD_STEREO 0x05 -#define AC97_RECORD_MONO 0x06 -#define AC97_RECORD_PHONE 0x07 - -/*#define AC97_RECORD_GAIN 0x1C*/ -#define AC97_RECORD_VOL_M 0x0F - -/*#define AC97_GENERAL_PURPOSE 0x20*/ -#define AC97_POWER_DOWN_CTRL 0x26 -#define AC97_ADC_READY 0x0001 -#define AC97_DAC_READY 0x0002 -#define AC97_ANALOG_READY 0x0004 -#define AC97_VREF_ON 0x0008 -#define AC97_PR0 0x0100 -#define AC97_PR1 0x0200 -#define AC97_PR2 0x0400 -#define AC97_PR3 0x0800 -#define AC97_PR4 0x1000 - -#define AC97_RESERVED1 0x28 - -#define AC97_VENDOR_TEST 0x5A - -#define AC97_CLOCK_DELAY 0x5C -#define AC97_LINEOUT_MUX_SEL 0x0001 -#define AC97_MONO_MUX_SEL 0x0002 -#define AC97_CLOCK_DELAY_SEL 0x1F -#define AC97_DAC_CDS_SHIFT 6 -#define AC97_ADC_CDS_SHIFT 11 - -#define AC97_MULTI_CHANNEL_SEL 0x74 - -/*#define AC97_VENDOR_ID1 0x7C -#define AC97_VENDOR_ID2 0x7E*/ - -/* - * ASSP control regs - */ -#define DSP_PORT_TIMER_COUNT 0x06 - -#define DSP_PORT_MEMORY_INDEX 0x80 - -#define DSP_PORT_MEMORY_TYPE 0x82 -#define MEMTYPE_INTERNAL_CODE 0x0002 -#define MEMTYPE_INTERNAL_DATA 0x0003 -#define MEMTYPE_MASK 0x0003 - -#define DSP_PORT_MEMORY_DATA 0x84 - -#define DSP_PORT_CONTROL_REG_A 0xA2 -#define DSP_PORT_CONTROL_REG_B 0xA4 -#define DSP_PORT_CONTROL_REG_C 0xA6 - -#define REV_A_CODE_MEMORY_BEGIN 0x0000 -#define REV_A_CODE_MEMORY_END 0x0FFF -#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 -#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) - -#define REV_B_CODE_MEMORY_BEGIN 0x0000 -#define REV_B_CODE_MEMORY_END 0x0BFF -#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 -#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) - -#define REV_A_DATA_MEMORY_BEGIN 0x1000 -#define REV_A_DATA_MEMORY_END 0x2FFF -#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 -#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) - -#define REV_B_DATA_MEMORY_BEGIN 0x1000 -#define REV_B_DATA_MEMORY_END 0x2BFF -#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 -#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) - - -#define NUM_UNITS_KERNEL_CODE 16 -#define NUM_UNITS_KERNEL_DATA 2 - -#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 -#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 - -/* - * Kernel data layout - */ - -#define DP_SHIFT_COUNT 7 - -#define KDATA_BASE_ADDR 0x1000 -#define KDATA_BASE_ADDR2 0x1080 - -#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) -#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) -#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) -#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) -#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) -#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) -#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) -#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) -#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) - -#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) -#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) - -#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) -#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) -#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) -#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) -#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) -#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) -#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) -#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) -#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) -#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) - -#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) -#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) - -#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) -#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) - -#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) -#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) - -#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) -#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) -#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) - -#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) -#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) -#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) -#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) -#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) - -#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) -#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) -#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) - -#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) -#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) -#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) - -#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) -#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) -#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) -#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) -#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) -#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) -#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) -#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) -#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) -#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) - -#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) -#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) -#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) - -#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) -#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) - -#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) -#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) -#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) - -#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) -#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) -#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) -#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) -#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) -#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) - -#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) -#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) -#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) -#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) -#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) -#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) - -#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) -#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) -#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) -#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) -#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) -#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) - -#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) -#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) -#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) -#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) - -#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) -#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) - -#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) -#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) - -#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) -#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) -#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) -#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) -#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) - -#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) -#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) - -#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) -#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) -#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) - -#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) -#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) - -#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) - -#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) -#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) -#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) -#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) -#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) -#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) -#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) -#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) -#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) -#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) -#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) -#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) - -#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) -#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) -#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) -#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) - -#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) -#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) - -#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) -#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) -#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) -#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) - -#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) -#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) -#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) -#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) -#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) - -/* - * second 'segment' (?) reserved for mixer - * buffers.. - */ - -#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) -#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) -#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) -#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) -#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) -#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) -#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) -#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) -#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) -#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) -#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) -#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) -#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) -#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) -#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) -#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) - -#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) -#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) -#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) -#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) -#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) -#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) -#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) -#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) -#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) -#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) -#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) - -#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) -#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) -#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) -#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) -#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) -#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) - -#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) -#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) -#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) -#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) - -/* - * client data area offsets - */ -#define CDATA_INSTANCE_READY 0x00 - -#define CDATA_HOST_SRC_ADDRL 0x01 -#define CDATA_HOST_SRC_ADDRH 0x02 -#define CDATA_HOST_SRC_END_PLUS_1L 0x03 -#define CDATA_HOST_SRC_END_PLUS_1H 0x04 -#define CDATA_HOST_SRC_CURRENTL 0x05 -#define CDATA_HOST_SRC_CURRENTH 0x06 - -#define CDATA_IN_BUF_CONNECT 0x07 -#define CDATA_OUT_BUF_CONNECT 0x08 - -#define CDATA_IN_BUF_BEGIN 0x09 -#define CDATA_IN_BUF_END_PLUS_1 0x0A -#define CDATA_IN_BUF_HEAD 0x0B -#define CDATA_IN_BUF_TAIL 0x0C -#define CDATA_OUT_BUF_BEGIN 0x0D -#define CDATA_OUT_BUF_END_PLUS_1 0x0E -#define CDATA_OUT_BUF_HEAD 0x0F -#define CDATA_OUT_BUF_TAIL 0x10 - -#define CDATA_DMA_CONTROL 0x11 -#define CDATA_RESERVED 0x12 - -#define CDATA_FREQUENCY 0x13 -#define CDATA_LEFT_VOLUME 0x14 -#define CDATA_RIGHT_VOLUME 0x15 -#define CDATA_LEFT_SUR_VOL 0x16 -#define CDATA_RIGHT_SUR_VOL 0x17 - -#define CDATA_HEADER_LEN 0x18 - -#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN -#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) -#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) -#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) -#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) -#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) -#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) -#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) - -#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) -#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) -#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) -#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) -#define MINISRC_BIQUAD_STAGE 2 -#define MINISRC_COEF_LOC 0X175 - -#define DMACONTROL_BLOCK_MASK 0x000F -#define DMAC_BLOCK0_SELECTOR 0x0000 -#define DMAC_BLOCK1_SELECTOR 0x0001 -#define DMAC_BLOCK2_SELECTOR 0x0002 -#define DMAC_BLOCK3_SELECTOR 0x0003 -#define DMAC_BLOCK4_SELECTOR 0x0004 -#define DMAC_BLOCK5_SELECTOR 0x0005 -#define DMAC_BLOCK6_SELECTOR 0x0006 -#define DMAC_BLOCK7_SELECTOR 0x0007 -#define DMAC_BLOCK8_SELECTOR 0x0008 -#define DMAC_BLOCK9_SELECTOR 0x0009 -#define DMAC_BLOCKA_SELECTOR 0x000A -#define DMAC_BLOCKB_SELECTOR 0x000B -#define DMAC_BLOCKC_SELECTOR 0x000C -#define DMAC_BLOCKD_SELECTOR 0x000D -#define DMAC_BLOCKE_SELECTOR 0x000E -#define DMAC_BLOCKF_SELECTOR 0x000F -#define DMACONTROL_PAGE_MASK 0x00F0 -#define DMAC_PAGE0_SELECTOR 0x0030 -#define DMAC_PAGE1_SELECTOR 0x0020 -#define DMAC_PAGE2_SELECTOR 0x0010 -#define DMAC_PAGE3_SELECTOR 0x0000 -#define DMACONTROL_AUTOREPEAT 0x1000 -#define DMACONTROL_STOPPED 0x2000 -#define DMACONTROL_DIRECTION 0x0100 - - -/* - * DSP Code images - */ - -static u16 assp_kernel_image[] = { - 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, - 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, - 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, - 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, - 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, - 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, - 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, - 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, - 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, - 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, - 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, - 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, - 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, - 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, - 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, - 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, - 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, - 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, - 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, - 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, - 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, - 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, - 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, - 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, - 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, - 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, - 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, - 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, - 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, - 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, - 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, - 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, - 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, - 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, - 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, - 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, - 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, - 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, - 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, - 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, - 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, - 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, - 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, - 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, - 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, - 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, - 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, - 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, - 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, - 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, - 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, - 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, - 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, - 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, - 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, - 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, - 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, - 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, - 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, - 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, - 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, - 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, - 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, - 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, - 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, - 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, - 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, - 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, - 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, - 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, - 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, - 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, - 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, - 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, - 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, - 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, - 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, - 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, - 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, - 0xBE3A, -}; - -/* - * Mini sample rate converter code image - * that is to be loaded at 0x400 on the DSP. - */ -static u16 assp_minisrc_image[] = { - - 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, - 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, - 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, - 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, - 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, - 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, - 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, - 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, - 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, - 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, - 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, - 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, - 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, - 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, - 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, - 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, - 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, - 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, - 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, - 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, - 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, - 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, - 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, - 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, - 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, - 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, - 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, - 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, - 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, - 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, - 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, - 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - diff --git a/sound/oss/maui.c b/sound/oss/maui.c deleted file mode 100644 index 317f22589a..0000000000 --- a/sound/oss/maui.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - * sound/oss/maui.c - * - * The low level driver for Turtle Beach Maui and Tropez. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Alan Cox General clean up, use kernel IRQ - * system - * Christoph Hellwig Adapted to module_init/module_exit - * Bartlomiej Zolnierkiewicz - * Added __init to download_code() - * - * Status: - * Andrew J. Kroll Tested 06/01/1999 with: - * * OSWF.MOT File Version: 1.15 - * * OSWF.MOT File Dated: 09/12/94 - * * Older versions will cause problems. - */ - -#include -#include -#include -#include - -#define USE_SEQ_MACROS -#define USE_SIMPLE_MACROS - -#include "sound_config.h" -#include "sound_firmware.h" - -#include "mpu401.h" - -static int maui_base = 0x330; - -static volatile int irq_ok; -static int *maui_osp; - -#define HOST_DATA_PORT (maui_base + 2) -#define HOST_STAT_PORT (maui_base + 3) -#define HOST_CTRL_PORT (maui_base + 3) - -#define STAT_TX_INTR 0x40 -#define STAT_TX_AVAIL 0x20 -#define STAT_TX_IENA 0x10 -#define STAT_RX_INTR 0x04 -#define STAT_RX_AVAIL 0x02 -#define STAT_RX_IENA 0x01 - -static int (*orig_load_patch)(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) = NULL; - -#include "maui_boot.h" - -static int maui_wait(int mask) -{ - int i; - - /* - * Perform a short initial wait without sleeping - */ - - for (i = 0; i < 100; i++) - if (inb(HOST_STAT_PORT) & mask) - return 1; - - /* - * Wait up to 15 seconds with sleeping - */ - - for (i = 0; i < 150; i++) { - if (inb(HOST_STAT_PORT) & mask) - return 1; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ / 10); - if (signal_pending(current)) - return 0; - } - return 0; -} - -static int maui_read(void) -{ - if (maui_wait(STAT_RX_AVAIL)) - return inb(HOST_DATA_PORT); - return -1; -} - -static int maui_write(unsigned char data) -{ - if (maui_wait(STAT_TX_AVAIL)) { - outb((data), HOST_DATA_PORT); - return 1; - } - printk(KERN_WARNING "Maui: Write timeout\n"); - return 0; -} - -static irqreturn_t mauiintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - irq_ok = 1; - return IRQ_HANDLED; -} - -static int __init download_code(void) -{ - int i, lines = 0; - int eol_seen = 0, done = 0; - int skip = 1; - - printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); - - for (i = 0; i < maui_osLen; i++) { - if (maui_os[i] != '\r') { - if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) { - skip = 0; - - if (maui_os[i] == '\n') - eol_seen = skip = 1; - else if (maui_os[i] == 'S') { - if (maui_os[i + 1] == '8') - done = 1; - if (!maui_write(0xF1)) - goto failure; - if (!maui_write('S')) - goto failure; - } else { - if (!maui_write(maui_os[i])) - goto failure; - } - - if (eol_seen) { - int c = 0; - int n; - - eol_seen = 0; - - for (n = 0; n < 2; n++) { - if (maui_wait(STAT_RX_AVAIL)) { - c = inb(HOST_DATA_PORT); - break; - } - } - if (c != 0x80) { - printk("Download not acknowledged\n"); - return 0; - } - else if (!(lines++ % 10)) - printk("."); - - if (done) { - printk("\n"); - printk(KERN_INFO "Download complete\n"); - return 1; - } - } - } - } - } - -failure: - printk("\n"); - printk(KERN_ERR "Download failed!!!\n"); - return 0; -} - -static int __init maui_init(int irq) -{ - unsigned char bits; - - switch (irq) { - case 9: - bits = 0x00; - break; - case 5: - bits = 0x08; - break; - case 12: - bits = 0x10; - break; - case 15: - bits = 0x18; - break; - - default: - printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); - return 0; - } - outb((0x00), HOST_CTRL_PORT); /* Reset */ - outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ - outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ - outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ - -#ifdef CONFIG_SMP - { - int i; - for (i = 0; i < 1000000 && !irq_ok; i++) - ; - if (!irq_ok) - return 0; - } -#endif - outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - - printk(KERN_INFO "Turtle Beach Maui initialization\n"); - - if (!download_code()) - return 0; - - outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ - - /* Select mpu401 mode */ - - maui_write(0xf0); - maui_write(1); - if (maui_read() != 0x80) { - maui_write(0xf0); - maui_write(1); - if (maui_read() != 0x80) - printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); - } - printk(KERN_INFO "Maui initialized OK\n"); - return 1; -} - -static int maui_short_wait(int mask) { - int i; - - for (i = 0; i < 1000; i++) { - if (inb(HOST_STAT_PORT) & mask) { - return 1; - } - } - return 0; -} - -static int maui_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) -{ - - struct sysex_info header; - unsigned long left, src_offs; - int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; - int i; - - if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ - return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); - - if (format != MAUI_PATCH) - { - printk(KERN_WARNING "Maui: Unknown patch format\n"); - } - if (count < hdr_size) { -/* printk("Maui error: Patch header too short\n");*/ - return -EINVAL; - } - count -= hdr_size; - - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ - - if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) - return -EFAULT; - - if (count < header.len) { - printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); - header.len = count; - } - left = header.len; - src_offs = 0; - - for (i = 0; i < left; i++) { - unsigned char data; - - if(get_user(*(unsigned char *) &data, (unsigned char __user *) &((addr)[hdr_size + i]))) - return -EFAULT; - if (i == 0 && !(data & 0x80)) - return -EINVAL; - - if (maui_write(data) == -1) - return -EIO; - } - - if ((i = maui_read()) != 0x80) { - if (i != -1) - printk("Maui: Error status %02x\n", i); - return -EIO; - } - return 0; -} - -static int __init probe_maui(struct address_info *hw_config) -{ - struct resource *ports; - int this_dev; - int i; - int tmp1, tmp2, ret; - - ports = request_region(hw_config->io_base, 2, "mpu401"); - if (!ports) - return 0; - - if (!request_region(hw_config->io_base + 2, 6, "Maui")) - goto out; - - maui_base = hw_config->io_base; - maui_osp = hw_config->osp; - - if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) - goto out2; - - /* - * Initialize the processor if necessary - */ - - if (maui_osLen > 0) { - if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || - !maui_write(0x9F) || /* Report firmware version */ - !maui_short_wait(STAT_RX_AVAIL) || - maui_read() == -1 || maui_read() == -1) - if (!maui_init(hw_config->irq)) - goto out3; - } - if (!maui_write(0xCF)) /* Report hardware version */ { - printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - goto out3; - } - if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { - printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - goto out3; - } - if (tmp1 == 0xff || tmp2 == 0xff) - goto out3; - printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); - - if (!maui_write(0x9F)) /* Report firmware version */ - goto out3; - if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) - goto out3; - - printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); - - if (!maui_write(0x85)) /* Report free DRAM */ - goto out3; - tmp1 = 0; - for (i = 0; i < 4; i++) { - tmp1 |= maui_read() << (7 * i); - } - printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); - - for (i = 0; i < 1000; i++) - if (probe_mpu401(hw_config, ports)) - break; - - ret = probe_mpu401(hw_config, ports); - if (!ret) - goto out3; - - conf_printf("Maui", hw_config); - - hw_config->irq *= -1; - hw_config->name = "Maui"; - attach_mpu401(hw_config, THIS_MODULE); - - if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ { - struct synth_operations *synth; - - this_dev = hw_config->slots[1]; - - /* - * Intercept patch loading calls so that they can be handled - * by the Maui driver. - */ - - synth = midi_devs[this_dev]->converter; - if (synth != NULL) { - synth->id = "MAUI"; - orig_load_patch = synth->load_patch; - synth->load_patch = &maui_load_patch; - } else - printk(KERN_ERR "Maui: Can't install patch loader\n"); - } - return 1; - -out3: - free_irq(hw_config->irq, NULL); -out2: - release_region(hw_config->io_base + 2, 6); -out: - release_region(hw_config->io_base, 2); - return 0; -} - -static void __exit unload_maui(struct address_info *hw_config) -{ - int irq = hw_config->irq; - release_region(hw_config->io_base + 2, 6); - unload_mpu401(hw_config); - - if (irq < 0) - irq = -irq; - if (irq > 0) - free_irq(irq, NULL); -} - -static int fw_load; - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; - -module_param(io, int, 0); -module_param(irq, int, 0); - -/* - * Install a Maui card. Needs mpu401 loaded already. - */ - -static int __init init_maui(void) -{ - printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - - if (cfg.io_base == -1 || cfg.irq == -1) { - printk(KERN_INFO "maui: irq and io must be set.\n"); - return -EINVAL; - } - - if (maui_os == NULL) { - fw_load = 1; - maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); - } - if (probe_maui(&cfg) == 0) - return -ENODEV; - - return 0; -} - -static void __exit cleanup_maui(void) -{ - if (fw_load && maui_os) - vfree(maui_os); - unload_maui(&cfg); -} - -module_init(init_maui); -module_exit(cleanup_maui); - -#ifndef MODULE -static int __init setup_maui(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} - -__setup("maui=", setup_maui); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/midi_syms.c b/sound/oss/midi_syms.c deleted file mode 100644 index 5b146ddf57..0000000000 --- a/sound/oss/midi_syms.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Exported symbols for midi driver. - */ - -#include - -char midi_syms_symbol; - -#include "sound_config.h" -#define _MIDI_SYNTH_C_ -#include "midi_synth.h" - -EXPORT_SYMBOL(do_midi_msg); -EXPORT_SYMBOL(midi_synth_open); -EXPORT_SYMBOL(midi_synth_close); -EXPORT_SYMBOL(midi_synth_ioctl); -EXPORT_SYMBOL(midi_synth_kill_note); -EXPORT_SYMBOL(midi_synth_start_note); -EXPORT_SYMBOL(midi_synth_set_instr); -EXPORT_SYMBOL(midi_synth_reset); -EXPORT_SYMBOL(midi_synth_hw_control); -EXPORT_SYMBOL(midi_synth_aftertouch); -EXPORT_SYMBOL(midi_synth_controller); -EXPORT_SYMBOL(midi_synth_panning); -EXPORT_SYMBOL(midi_synth_setup_voice); -EXPORT_SYMBOL(midi_synth_send_sysex); -EXPORT_SYMBOL(midi_synth_bender); -EXPORT_SYMBOL(midi_synth_load_patch); -EXPORT_SYMBOL(MIDIbuf_avail); diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c index d2ab5c08b6..9e450988ed 100644 --- a/sound/oss/midi_synth.c +++ b/sound/oss/midi_synth.c @@ -84,6 +84,7 @@ do_midi_msg(int synthno, unsigned char *msg, int mlen) ; } } +EXPORT_SYMBOL(do_midi_msg); static void midi_outc(int midi_dev, int data) @@ -276,6 +277,7 @@ int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg) return -EINVAL; } } +EXPORT_SYMBOL(midi_synth_ioctl); int midi_synth_kill_note(int dev, int channel, int note, int velocity) @@ -342,6 +344,7 @@ midi_synth_kill_note(int dev, int channel, int note, int velocity) return 0; } +EXPORT_SYMBOL(midi_synth_kill_note); int midi_synth_set_instr(int dev, int channel, int instr_no) @@ -364,6 +367,7 @@ midi_synth_set_instr(int dev, int channel, int instr_no) return 0; } +EXPORT_SYMBOL(midi_synth_set_instr); int midi_synth_start_note(int dev, int channel, int note, int velocity) @@ -405,6 +409,7 @@ midi_synth_start_note(int dev, int channel, int note, int velocity) } return 0; } +EXPORT_SYMBOL(midi_synth_start_note); void midi_synth_reset(int dev) @@ -412,6 +417,7 @@ midi_synth_reset(int dev) leave_sysex(dev); } +EXPORT_SYMBOL(midi_synth_reset); int midi_synth_open(int dev, int mode) @@ -444,6 +450,7 @@ midi_synth_open(int dev, int mode) return 1; } +EXPORT_SYMBOL(midi_synth_open); void midi_synth_close(int dev) @@ -459,11 +466,13 @@ midi_synth_close(int dev) midi_devs[orig_dev]->close(orig_dev); } +EXPORT_SYMBOL(midi_synth_close); void midi_synth_hw_control(int dev, unsigned char *event) { } +EXPORT_SYMBOL(midi_synth_hw_control); int midi_synth_load_patch(int dev, int format, const char __user *addr, @@ -542,11 +551,13 @@ midi_synth_load_patch(int dev, int format, const char __user *addr, midi_outc(orig_dev, 0xf7); return 0; } - +EXPORT_SYMBOL(midi_synth_load_patch); + void midi_synth_panning(int dev, int channel, int pressure) { } - +EXPORT_SYMBOL(midi_synth_panning); + void midi_synth_aftertouch(int dev, int channel, int pressure) { int orig_dev = synth_devs[dev]->midi_dev; @@ -576,6 +587,7 @@ void midi_synth_aftertouch(int dev, int channel, int pressure) midi_outc(orig_dev, pressure); } +EXPORT_SYMBOL(midi_synth_aftertouch); void midi_synth_controller(int dev, int channel, int ctrl_num, int value) @@ -604,6 +616,7 @@ midi_synth_controller(int dev, int channel, int ctrl_num, int value) midi_outc(orig_dev, ctrl_num); midi_outc(orig_dev, value & 0x7f); } +EXPORT_SYMBOL(midi_synth_controller); void midi_synth_bender(int dev, int channel, int value) @@ -635,11 +648,13 @@ midi_synth_bender(int dev, int channel, int value) midi_outc(orig_dev, value & 0x7f); midi_outc(orig_dev, (value >> 7) & 0x7f); } +EXPORT_SYMBOL(midi_synth_bender); void midi_synth_setup_voice(int dev, int voice, int channel) { } +EXPORT_SYMBOL(midi_synth_setup_voice); int midi_synth_send_sysex(int dev, unsigned char *bytes, int len) @@ -695,3 +710,5 @@ midi_synth_send_sysex(int dev, unsigned char *bytes, int len) return 0; } +EXPORT_SYMBOL(midi_synth_send_sysex); + diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c index c0e4bbc22c..a40be0cf1d 100644 --- a/sound/oss/midibuf.c +++ b/sound/oss/midibuf.c @@ -414,18 +414,11 @@ unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) } -void MIDIbuf_init(void) -{ - /* drag in midi_syms.o */ - { - extern char midi_syms_symbol; - midi_syms_symbol = 0; - } -} - int MIDIbuf_avail(int dev) { if (midi_in_buf[dev]) return DATA_AVAIL (midi_in_buf[dev]); return 0; } +EXPORT_SYMBOL(MIDIbuf_avail); + diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c index 321f4c4b5a..162d07cc48 100644 --- a/sound/oss/mpu401.c +++ b/sound/oss/mpu401.c @@ -432,16 +432,7 @@ static void mpu401_input_loop(struct mpu_config *devc) devc->m_busy = 0; } -int intchk_mpu401(void *dev_id) -{ - struct mpu_config *devc; - int dev = (int) dev_id; - - devc = &dev_conf[dev]; - return input_avail(devc); -} - -irqreturn_t mpuintr(int irq, void *dev_id, struct pt_regs *dummy) +static irqreturn_t mpuintr(int irq, void *dev_id, struct pt_regs *dummy) { struct mpu_config *devc; int dev = (int) dev_id; @@ -1761,8 +1752,6 @@ static int mpu_timer_init(int midi_dev) EXPORT_SYMBOL(probe_mpu401); EXPORT_SYMBOL(attach_mpu401); EXPORT_SYMBOL(unload_mpu401); -EXPORT_SYMBOL(intchk_mpu401); -EXPORT_SYMBOL(mpuintr); static struct address_info cfg; diff --git a/sound/oss/mpu401.h b/sound/oss/mpu401.h index bdc5bde641..84c0e9522e 100644 --- a/sound/oss/mpu401.h +++ b/sound/oss/mpu401.h @@ -10,5 +10,3 @@ int probe_mpu401(struct address_info *hw_config, struct resource *ports); int attach_mpu401(struct address_info * hw_config, struct module *owner); void unload_mpu401(struct address_info *hw_info); -int intchk_mpu401(void *dev_id); -irqreturn_t mpuintr(int irq, void *dev_id, struct pt_regs * dummy); diff --git a/sound/oss/opl3sa.c b/sound/oss/opl3sa.c deleted file mode 100644 index 2535ed0b5f..0000000000 --- a/sound/oss/opl3sa.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * sound/oss/opl3sa.c - * - * Low level driver for Yamaha YMF701B aka OPL3-SA chip - * - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Alan Cox Modularisation - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo got rid of attach_uart401 - * - * FIXME: - * Check for install of mpu etc is wrong, should check result of the mss stuff - */ - -#include -#include -#include - -#undef SB_OK - -#include "sound_config.h" - -#include "ad1848.h" -#include "mpu401.h" - -#ifdef SB_OK -#include "sb.h" -static int sb_initialized; -#endif - -static DEFINE_SPINLOCK(lock); - -static unsigned char opl3sa_read(int addr) -{ - unsigned long flags; - unsigned char tmp; - - spin_lock_irqsave(&lock,flags); - outb((0x1d), 0xf86); /* password */ - outb(((unsigned char) addr), 0xf86); /* address */ - tmp = inb(0xf87); /* data */ - spin_unlock_irqrestore(&lock,flags); - - return tmp; -} - -static void opl3sa_write(int addr, int data) -{ - unsigned long flags; - - spin_lock_irqsave(&lock,flags); - outb((0x1d), 0xf86); /* password */ - outb(((unsigned char) addr), 0xf86); /* address */ - outb(((unsigned char) data), 0xf87); /* data */ - spin_unlock_irqrestore(&lock,flags); -} - -static int __init opl3sa_detect(void) -{ - int tmp; - - if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) - { - DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); - /* return 0; */ - } - - /* - * Check that the password feature has any effect - */ - - if (inb(0xf87) == tmp) - { - DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); - return 0; - } - tmp = (opl3sa_read(0x04) & 0xe0) >> 5; - - if (tmp != 0 && tmp != 1) - { - DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); - return 0; - } - DDB(printk("OPL3-SA mode %x detected\n", tmp)); - - opl3sa_write(0x01, 0x00); /* Disable MSS */ - opl3sa_write(0x02, 0x00); /* Disable SB */ - opl3sa_write(0x03, 0x00); /* Disable MPU */ - - return 1; -} - -/* - * Probe and attach routines for the Windows Sound System mode of - * OPL3-SA - */ - -static int __init probe_opl3sa_wss(struct address_info *hw_config, struct resource *ports) -{ - unsigned char tmp = 0x24; /* WSS enable */ - - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (OPL3-SA for example) - * return 0x00. - */ - - if (!opl3sa_detect()) - { - printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); - return 0; - } - - switch (hw_config->io_base) - { - case 0x530: - tmp |= 0x00; - break; - case 0xe80: - tmp |= 0x08; - break; - case 0xf40: - tmp |= 0x10; - break; - case 0x604: - tmp |= 0x18; - break; - default: - printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); - return 0; - } - - opl3sa_write(0x01, tmp); /* WSS setup register */ - - return probe_ms_sound(hw_config, ports); -} - -static void __init attach_opl3sa_wss(struct address_info *hw_config, struct resource *ports) -{ - int nm = num_mixers; - - /* FIXME */ - attach_ms_sound(hw_config, ports, THIS_MODULE); - if (num_mixers > nm) /* A mixer was installed */ - { - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); - } -} - - -static int __init probe_opl3sa_mpu(struct address_info *hw_config) -{ - unsigned char conf; - static signed char irq_bits[] = { - -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 - }; - - if (hw_config->irq > 10) - { - printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - if (irq_bits[hw_config->irq] == -1) - { - printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - switch (hw_config->io_base) - { - case 0x330: - conf = 0x00; - break; - case 0x332: - conf = 0x20; - break; - case 0x334: - conf = 0x40; - break; - case 0x300: - conf = 0x60; - break; - default: - return 0; /* Invalid port */ - } - - conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ - conf |= irq_bits[hw_config->irq] << 2; - - opl3sa_write(0x03, conf); - - hw_config->name = "OPL3-SA (MPU401)"; - - return probe_uart401(hw_config, THIS_MODULE); -} - -static void __exit unload_opl3sa_wss(struct address_info *hw_config) -{ - int dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = hw_config->dma; - - release_region(0xf86, 2); - release_region(hw_config->io_base, 4); - - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - dma2, - 0); - sound_unload_audiodev(hw_config->slots[0]); -} - -static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config) -{ - unload_uart401(hw_config); -} - -#ifdef SB_OK -static inline void __exit unload_opl3sa_sb(struct address_info *hw_config) -{ - sb_dsp_unload(hw_config); -} -#endif - -static int found_mpu; - -static struct address_info cfg; -static struct address_info cfg_mpu; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata mpu_io = -1; -static int __initdata mpu_irq = -1; - -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(dma, int, 0); -module_param(dma2, int, 0); -module_param(mpu_io, int, 0); -module_param(mpu_irq, int, 0); - -static int __init init_opl3sa(void) -{ - struct resource *ports; - if (io == -1 || irq == -1 || dma == -1) { - printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); - return -EINVAL; - } - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - ports = request_region(io + 4, 4, "ad1848"); - if (!ports) - return -EBUSY; - - if (!request_region(0xf86, 2, "OPL3-SA"))/* Control port is busy */ { - release_region(io + 4, 4); - return 0; - } - - if (!request_region(io, 4, "WSS config")) { - release_region(0x86, 2); - release_region(io + 4, 4); - return 0; - } - - if (probe_opl3sa_wss(&cfg, ports) == 0) { - release_region(0xf86, 2); - release_region(io, 4); - release_region(io + 4, 4); - return -ENODEV; - } - - found_mpu=probe_opl3sa_mpu(&cfg_mpu); - - attach_opl3sa_wss(&cfg, ports); - return 0; -} - -static void __exit cleanup_opl3sa(void) -{ - if(found_mpu) - unload_opl3sa_mpu(&cfg_mpu); - unload_opl3sa_wss(&cfg); -} - -module_init(init_opl3sa); -module_exit(cleanup_opl3sa); - -#ifndef MODULE -static int __init setup_opl3sa(char *str) -{ - /* io, irq, dma, dma2, mpu_io, mpu_irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - mpu_io = ints[5]; - mpu_irq = ints[6]; - - return 1; -} - -__setup("opl3sa=", setup_opl3sa); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/rme96xx.c b/sound/oss/rme96xx.c deleted file mode 100644 index f17d25b6f8..0000000000 --- a/sound/oss/rme96xx.c +++ /dev/null @@ -1,1857 +0,0 @@ -/* (C) 2000 Guenter Geiger - with copy/pastes from the driver of Winfried Ritsch - based on es1370.c - - - - * 10 Jan 2001: 0.1 initial version - * 19 Jan 2001: 0.2 fixed bug in select() - * 27 Apr 2001: 0.3 more than one card usable - * 11 May 2001: 0.4 fixed for SMP, included into kernel source tree - * 17 May 2001: 0.5 draining code didn't work on new cards - * 18 May 2001: 0.6 remove synchronize_irq() call - * 17 Jul 2001: 0.7 updated xrmectrl to make it work for newer cards - * 2 feb 2002: 0.8 fixed pci device handling, see below for patches from Heiko (Thanks!) - Marcus Meissner - - Modifications - Heiko Purnhagen - HP20020108 fixed handling of "large" read() - HP20020116 towards REV 1.5 support, based on ALSA's card-rme9652.c - HP20020118 made mixer ioctl and handling of devices>1 more safe - HP20020201 fixed handling of "large" read() properly - added REV 1.5 S/P-DIF receiver support - SNDCTL_DSP_SPEED now returns the actual speed - * 10 Aug 2002: added synchronize_irq() again - -TODO: - - test more than one card --- done - - check for pci IOREGION (see es1370) in rme96xx_probe ?? - - error detection - - mmap interface - - mixer mmap interface - - mixer ioctl - - get rid of noise upon first open (why ??) - - allow multiple open (at least for read) - - allow multiple open for non overlapping regions - - recheck the multiple devices part (offsets of different devices, etc) - - do decent draining in _release --- done - - SMP support - - what about using fragstotal>2 for small fragsize? (HP20020118) - - add support for AFMT_S32_LE -*/ - -#ifndef RMEVERSION -#define RMEVERSION "0.8" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "rme96xx.h" - -#define NR_DEVICE 2 - -static int devices = 1; -module_param(devices, int, 0); -MODULE_PARM_DESC(devices, "number of dsp devices allocated by the driver"); - - -MODULE_AUTHOR("Guenter Geiger, geiger@debian.org"); -MODULE_DESCRIPTION("RME9652/36 \"Hammerfall\" Driver"); -MODULE_LICENSE("GPL"); - - -#ifdef DEBUG -#define DBG(x) printk("RME_DEBUG:");x -#define COMM(x) printk("RME_COMM: " x "\n"); -#else -#define DBG(x) while (0) {} -#define COMM(x) -#endif - -/*-------------------------------------------------------------------------- - Preporcessor Macros and Definitions - --------------------------------------------------------------------------*/ - -#define RME96xx_MAGIC 0x6473 - -/* Registers-Space in offsets from base address with 16MByte size */ - -#define RME96xx_IO_EXTENT 16l*1024l*1024l -#define RME96xx_CHANNELS_PER_CARD 26 - -/* Write - Register */ - -/* 0,4,8,12,16,20,24,28 ... hardware init (erasing fifo-pointer intern) */ -#define RME96xx_num_of_init_regs 8 - -#define RME96xx_init_buffer (0/4) -#define RME96xx_play_buffer (32/4) /* pointer to 26x64kBit RAM from mainboard */ -#define RME96xx_rec_buffer (36/4) /* pointer to 26x64kBit RAM from mainboard */ -#define RME96xx_control_register (64/4) /* exact meaning see below */ -#define RME96xx_irq_clear (96/4) /* irq acknowledge */ -#define RME96xx_time_code (100/4) /* if used with alesis adat */ -#define RME96xx_thru_base (128/4) /* 132...228 Thru for 26 channels */ -#define RME96xx_thru_channels RME96xx_CHANNELS_PER_CARD - -/* Read Register */ - -#define RME96xx_status_register 0 /* meaning see below */ - - - -/* Status Register: */ -/* ------------------------------------------------------------------------ */ -#define RME96xx_IRQ 0x0000001 /* IRQ is High if not reset by RMExx_irq_clear */ -#define RME96xx_lock_2 0x0000002 /* ADAT 3-PLL: 1=locked, 0=unlocked */ -#define RME96xx_lock_1 0x0000004 /* ADAT 2-PLL: 1=locked, 0=unlocked */ -#define RME96xx_lock_0 0x0000008 /* ADAT 1-PLL: 1=locked, 0=unlocked */ - -#define RME96xx_fs48 0x0000010 /* sample rate 0 ...44.1/88.2, 1 ... 48/96 Khz */ -#define RME96xx_wsel_rd 0x0000020 /* if Word-Clock is used and valid then 1 */ -#define RME96xx_buf_pos1 0x0000040 /* Bit 6..15 : Position of buffer-pointer in 64Bytes-blocks */ -#define RME96xx_buf_pos2 0x0000080 /* resolution +/- 1 64Byte/block (since 64Bytes bursts) */ - -#define RME96xx_buf_pos3 0x0000100 /* 10 bits = 1024 values */ -#define RME96xx_buf_pos4 0x0000200 /* if we mask off the first 6 bits, we can take the status */ -#define RME96xx_buf_pos5 0x0000400 /* register as sample counter in the hardware buffer */ -#define RME96xx_buf_pos6 0x0000800 - -#define RME96xx_buf_pos7 0x0001000 -#define RME96xx_buf_pos8 0x0002000 -#define RME96xx_buf_pos9 0x0004000 -#define RME96xx_buf_pos10 0x0008000 - -#define RME96xx_sync_2 0x0010000 /* if ADAT-IN3 synced to system clock */ -#define RME96xx_sync_1 0x0020000 /* if ADAT-IN2 synced to system clock */ -#define RME96xx_sync_0 0x0040000 /* if ADAT-IN1 synced to system clock */ -#define RME96xx_DS_rd 0x0080000 /* 1=Double Speed, 0=Normal Speed */ - -#define RME96xx_tc_busy 0x0100000 /* 1=time-code copy in progress (960ms) */ -#define RME96xx_tc_out 0x0200000 /* time-code out bit */ -#define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */ -#define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ - -#define RME96xx_F_2 0x1000000 /* 001=Rev 1.5+ external Crystal Chip */ -#define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/ -#define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */ -#define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */ -#define RME96xx_SPDIF_READ 0x10000000 /* byte available from Rev 1.5+ SPDIF interface */ - -/* Status Register Fields */ - -#define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2) -#define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2) -#define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2) -#define rme96xx_decode_spdif_rate(x) ((x)>>22) - -/* Bit 6..15 : h/w buffer pointer */ -#define RME96xx_buf_pos 0x000FFC0 -/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later - Rev G EEPROMS and Rev 1.5 cards or later. -*/ -#define RME96xx_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME96xx_buf_pos)) - - -/* Control-Register: */ -/*--------------------------------------------------------------------------------*/ - -#define RME96xx_start_bit 0x0001 /* start record/play */ -#define RME96xx_latency0 0x0002 /* Buffer size / latency */ -#define RME96xx_latency1 0x0004 /* buffersize = 512Bytes * 2^n */ -#define RME96xx_latency2 0x0008 /* 0=64samples ... 7=8192samples */ - -#define RME96xx_Master 0x0010 /* Clock Mode 1=Master, 0=Slave/Auto */ -#define RME96xx_IE 0x0020 /* Interupt Enable */ -#define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/ -#define RME96xx_freq1 0x0080 /* samplerate 0=32 kHz, 1=other rates ??? (from ALSA, but may be wrong) */ -#define RME96xx_DS 0x0100 /* double speed 0=44.1/48, 1=88.2/96 Khz */ -#define RME96xx_PRO 0x0200 /* SPDIF-OUT 0=consumer, 1=professional */ -#define RME96xx_EMP 0x0400 /* SPDIF-OUT emphasis 0=off, 1=on */ -#define RME96xx_Dolby 0x0800 /* SPDIF-OUT non-audio bit 1=set, 0=unset */ - -#define RME96xx_opt_out 0x1000 /* use 1st optical OUT as SPDIF: 1=yes, 0=no */ -#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master) */ -#define RME96xx_inp_0 0x4000 /* SPDIF-IN 00=optical (ADAT1), */ -#define RME96xx_inp_1 0x8000 /* 01=coaxial (Cinch), 10=internal CDROM */ - -#define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */ -#define RME96xx_SyncRef1 0x20000 /* 00=ADAT1, 01=ADAT2, 10=ADAT3, 11=SPDIF */ - -#define RME96xx_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w SPDIF receiver */ -#define RME96xx_SPDIF_SELECT (1<<19) -#define RME96xx_SPDIF_CLOCK (1<<20) -#define RME96xx_SPDIF_WRITE (1<<21) -#define RME96xx_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */ - - -#define RME96xx_ctrl_init (RME96xx_latency0 |\ - RME96xx_Master |\ - RME96xx_inp_1) - - - -/* Control register fields and shortcuts */ - -#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2) -#define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1) -#define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1) -#define RME96xx_mixer_allowed (RME96xx_Master|RME96xx_PRO|RME96xx_EMP|RME96xx_Dolby|RME96xx_opt_out|RME96xx_wsel|RME96xx_inp|RME96xx_SyncRef|RME96xx_ADAT1_INTERNAL) - -/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit1 (??? HP20020201) */ - -#define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1) -#define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7) -#define RME96xx_SET_inp(x) (((x)&0x3)<<14) -#define RME96xx_GET_inp(x) (((x)>>14)&0x3) -#define RME96xx_SET_SyncRef(x) (((x)&0x3)<<17) -#define RME96xx_GET_SyncRef(x) (((x)>>17)&0x3) - - -/* buffer sizes */ -#define RME96xx_BYTES_PER_SAMPLE 4 /* sizeof(u32) */ -#define RME_16K 16*1024 - -#define RME96xx_DMA_MAX_SAMPLES (RME_16K) -#define RME96xx_DMA_MAX_SIZE (RME_16K * RME96xx_BYTES_PER_SAMPLE) -#define RME96xx_DMA_MAX_SIZE_ALL (RME96xx_DMA_MAX_SIZE * RME96xx_CHANNELS_PER_CARD) - -#define RME96xx_NUM_OF_FRAGMENTS 2 -#define RME96xx_FRAGMENT_MAX_SIZE (RME96xx_DMA_MAX_SIZE/2) -#define RME96xx_FRAGMENT_MAX_SAMPLES (RME96xx_DMA_MAX_SAMPLES/2) -#define RME96xx_MAX_LATENCY 7 /* 16k samples */ - - -#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */ -#define RME96xx_MASK_DEVS 0x3 /* RME96xx_MAX_DEVS-1 */ - -#define RME_MESS "rme96xx:" -/*------------------------------------------------------------------------ - Types, struct and function declarations - ------------------------------------------------------------------------*/ - - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT RME_MESS" invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != RME96xx_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - - -static struct file_operations rme96xx_audio_fops; -static struct file_operations rme96xx_mixer_fops; -static int numcards; - -typedef int32_t raw_sample_t; - -typedef struct _rme96xx_info { - - /* hardware settings */ - int magic; - struct pci_dev * pcidev; /* pci_dev structure */ - unsigned long __iomem *iobase; - unsigned int irq; - - /* list of rme96xx devices */ - struct list_head devs; - - spinlock_t lock; - - u32 *recbuf; /* memory for rec buffer */ - u32 *playbuf; /* memory for play buffer */ - - u32 control_register; - - u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ - - int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */ - char *card_name; /* hammerfall or hammerfall light names */ - - int open_count; /* unused ??? HP20020201 */ - - int rate; - int latency; - unsigned int fragsize; - int started; - - int hwptr; /* can be negativ because of pci burst offset */ - unsigned int hwbufid; /* set by interrupt, buffer which is written/read now */ - - struct dmabuf { - - unsigned int format; - int formatshift; - int inchannels; /* number of channels for device */ - int outchannels; /* number of channels for device */ - int mono; /* if true, we play mono on 2 channels */ - int inoffset; /* which channel is considered the first one */ - int outoffset; - - /* state */ - int opened; /* open() made */ - int started; /* first write/read */ - int mmapped; /* mmap */ - int open_mode; - - struct _rme96xx_info *s; - - /* pointer to read/write position in buffer */ - unsigned readptr; - unsigned writeptr; - - unsigned error; /* over/underruns cleared on sync again */ - - /* waiting and locking */ - wait_queue_head_t wait; - struct mutex open_mutex; - wait_queue_head_t open_wait; - - } dma[RME96xx_MAX_DEVS]; - - int dspnum[RME96xx_MAX_DEVS]; /* register with sound subsystem */ - int mixer; /* register with sound subsystem */ -} rme96xx_info; - - -/* fiddling with the card (first level hardware control) */ - -static inline void rme96xx_set_ctrl(rme96xx_info* s,int mask) -{ - - s->control_register|=mask; - writel(s->control_register,s->iobase + RME96xx_control_register); - -} - -static inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask) -{ - - s->control_register&=(~mask); - writel(s->control_register,s->iobase + RME96xx_control_register); - -} - -static inline int rme96xx_get_sample_rate_status(rme96xx_info* s) -{ - int val; - u32 status; - status = readl(s->iobase + RME96xx_status_register); - val = (status & RME96xx_fs48) ? 48000 : 44100; - if (status & RME96xx_DS_rd) - val *= 2; - return val; -} - -static inline int rme96xx_get_sample_rate_ctrl(rme96xx_info* s) -{ - int val; - val = (s->control_register & RME96xx_freq) ? 48000 : 44100; - if (s->control_register & RME96xx_DS) - val *= 2; - return val; -} - - -/* code from ALSA card-rme9652.c for rev 1.5 SPDIF receiver HP 20020201 */ - -static void rme96xx_spdif_set_bit (rme96xx_info* s, int mask, int onoff) -{ - if (onoff) - s->control_register |= mask; - else - s->control_register &= ~mask; - - writel(s->control_register,s->iobase + RME96xx_control_register); -} - -static void rme96xx_spdif_write_byte (rme96xx_info* s, const int val) -{ - long mask; - long i; - - for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { - if (val & mask) - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_WRITE, 1); - else - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_WRITE, 0); - - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 1); - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 0); - } -} - -static int rme96xx_spdif_read_byte (rme96xx_info* s) -{ - long mask; - long val; - long i; - - val = 0; - - for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 1); - if (readl(s->iobase + RME96xx_status_register) & RME96xx_SPDIF_READ) - val |= mask; - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 0); - } - - return val; -} - -static void rme96xx_write_spdif_codec (rme96xx_info* s, const int address, const int data) -{ - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1); - rme96xx_spdif_write_byte (s, 0x20); - rme96xx_spdif_write_byte (s, address); - rme96xx_spdif_write_byte (s, data); - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0); -} - - -static int rme96xx_spdif_read_codec (rme96xx_info* s, const int address) -{ - int ret; - - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1); - rme96xx_spdif_write_byte (s, 0x20); - rme96xx_spdif_write_byte (s, address); - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0); - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1); - - rme96xx_spdif_write_byte (s, 0x21); - ret = rme96xx_spdif_read_byte (s); - rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0); - - return ret; -} - -static void rme96xx_initialize_spdif_receiver (rme96xx_info* s) -{ - /* XXX what unsets this ? */ - /* no idea ??? HP 20020201 */ - - s->control_register |= RME96xx_SPDIF_RESET; - - rme96xx_write_spdif_codec (s, 4, 0x40); - rme96xx_write_spdif_codec (s, 17, 0x13); - rme96xx_write_spdif_codec (s, 6, 0x02); -} - -static inline int rme96xx_spdif_sample_rate (rme96xx_info *s, int *spdifrate) -{ - unsigned int rate_bits; - - *spdifrate = 0x1; - if (readl(s->iobase + RME96xx_status_register) & RME96xx_ERF) { - return -1; /* error condition */ - } - - if (s->hw_rev == 15) { - - int x, y, ret; - - x = rme96xx_spdif_read_codec (s, 30); - - if (x != 0) - y = 48000 * 64 / x; - else - y = 0; - - if (y > 30400 && y < 33600) {ret = 32000; *spdifrate = 0x7;} - else if (y > 41900 && y < 46000) {ret = 44100; *spdifrate = 0x6;} - else if (y > 46000 && y < 50400) {ret = 48000; *spdifrate = 0x5;} - else if (y > 60800 && y < 67200) {ret = 64000; *spdifrate = 0x0;} - else if (y > 83700 && y < 92000) {ret = 88200; *spdifrate = 0x4;} - else if (y > 92000 && y < 100000) {ret = 96000; *spdifrate = 0x3;} - else {ret = 0; *spdifrate = 0x1;} - return ret; - } - - rate_bits = readl(s->iobase + RME96xx_status_register) & RME96xx_F; - - switch (*spdifrate = rme96xx_decode_spdif_rate(rate_bits)) { - case 0x7: - return 32000; - break; - - case 0x6: - return 44100; - break; - - case 0x5: - return 48000; - break; - - case 0x4: - return 88200; - break; - - case 0x3: - return 96000; - break; - - case 0x0: - return 64000; - break; - - default: - /* was an ALSA warning ... - snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n", - s->card_name, rate_bits); - */ - return 0; - break; - } -} - -/* end of code from ALSA card-rme9652.c */ - - - -/* the hwbuf in the status register seems to have some jitter, to get rid of - it, we first only let the numbers grow, to be on the secure side we - subtract a certain amount RME96xx_BURSTBYTES from the resulting number */ - -/* the function returns the hardware pointer in bytes */ -#define RME96xx_BURSTBYTES -64 /* bytes by which hwptr could be off */ - -static inline int rme96xx_gethwptr(rme96xx_info* s,int exact) -{ - unsigned long flags; - if (exact) { - unsigned int hwp; -/* the hwptr seems to be rather unreliable :(, so we don't use it */ - spin_lock_irqsave(&s->lock,flags); - - hwp = readl(s->iobase + RME96xx_status_register) & 0xffc0; - s->hwptr = (hwp < s->hwptr) ? s->hwptr : hwp; -// s->hwptr = hwp; - - spin_unlock_irqrestore(&s->lock,flags); - return (s->hwptr+RME96xx_BURSTBYTES) & ((s->fragsize<<1)-1); - } - return (s->hwbufid ? s->fragsize : 0); -} - -static inline void rme96xx_setlatency(rme96xx_info* s,int l) -{ - s->latency = l; - s->fragsize = 1<<(8+l); - rme96xx_unset_ctrl(s,RME96xx_latency); - rme96xx_set_ctrl(s,RME96xx_SET_LATENCY(l)); -} - - -static void rme96xx_clearbufs(struct dmabuf* dma) -{ - int i,j; - unsigned long flags; - - /* clear dmabufs */ - for(i=0;ioutchannels + dma->mono;j++) - memset(&dma->s->playbuf[(dma->outoffset + j)*RME96xx_DMA_MAX_SAMPLES], - 0, RME96xx_DMA_MAX_SIZE); - } - spin_lock_irqsave(&dma->s->lock,flags); - dma->writeptr = 0; - dma->readptr = 0; - spin_unlock_irqrestore(&dma->s->lock,flags); -} - -static int rme96xx_startcard(rme96xx_info *s,int stop) -{ - int i; - unsigned long flags; - - COMM ("startcard"); - if(s->control_register & RME96xx_IE){ - /* disable interrupt first */ - - rme96xx_unset_ctrl( s,RME96xx_start_bit ); - udelay(10); - rme96xx_unset_ctrl( s,RME96xx_IE); - spin_lock_irqsave(&s->lock,flags); /* timing is critical */ - s->started = 0; - spin_unlock_irqrestore(&s->lock,flags); - if (stop) { - COMM("Sound card stopped"); - return 1; - } - } - COMM ("interrupt disabled"); - /* first initialize all pointers on card */ - for(i=0;iiobase + i); - udelay(10); /* ?? */ - } - COMM ("regs cleaned"); - - spin_lock_irqsave(&s->lock,flags); /* timing is critical */ - udelay(10); - s->started = 1; - s->hwptr = 0; - spin_unlock_irqrestore(&s->lock,flags); - - rme96xx_set_ctrl( s, RME96xx_IE | RME96xx_start_bit); - - - COMM("Sound card started"); - - return 1; -} - - -static inline int rme96xx_getospace(struct dmabuf * dma, unsigned int hwp) -{ - int cnt; - int swptr; - unsigned long flags; - - spin_lock_irqsave(&dma->s->lock,flags); - swptr = dma->writeptr; - cnt = (hwp - swptr); - - if (cnt < 0) { - cnt = ((dma->s->fragsize<<1) - swptr); - } - spin_unlock_irqrestore(&dma->s->lock,flags); - return cnt; -} - -static inline int rme96xx_getispace(struct dmabuf * dma, unsigned int hwp) -{ - int cnt; - int swptr; - unsigned long flags; - - spin_lock_irqsave(&dma->s->lock,flags); - swptr = dma->readptr; - cnt = (hwp - swptr); - - if (cnt < 0) { - cnt = ((dma->s->fragsize<<1) - swptr); - } - spin_unlock_irqrestore(&dma->s->lock,flags); - return cnt; -} - - -static inline int rme96xx_copyfromuser(struct dmabuf* dma,const char __user * buffer,int count,int hop) -{ - int swptr = dma->writeptr; - switch (dma->format) { - case AFMT_S32_BLOCKED: - { - char __user * buf = (char __user *)buffer; - int cnt = count/dma->outchannels; - int i; - for (i=0;i < dma->outchannels;i++) { - char* hwbuf =(char*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=swptr; - - if (copy_from_user(hwbuf,buf, cnt)) - return -1; - buf+=hop; - } - swptr+=cnt; - break; - } - case AFMT_S16_LE: - { - int i,j; - int cnt = count/dma->outchannels; - for (i=0;i < dma->outchannels + dma->mono;i++) { - short __user * sbuf = (short __user *)buffer + i*(!dma->mono); - short* hwbuf =(short*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=(swptr>>1); - for (j=0;j<(cnt>>1);j++) { - hwbuf++; /* skip the low 16 bits */ - __get_user(*hwbuf++,sbuf++); - sbuf+=(dma->outchannels-1); - } - } - swptr += (cnt<<1); - break; - } - default: - printk(RME_MESS" unsupported format\n"); - return -1; - } /* switch */ - - swptr&=((dma->s->fragsize<<1) -1); - dma->writeptr = swptr; - - return 0; -} - -/* The count argument is the number of bytes */ -static inline int rme96xx_copytouser(struct dmabuf* dma,const char __user* buffer,int count,int hop) -{ - int swptr = dma->readptr; - switch (dma->format) { - case AFMT_S32_BLOCKED: - { - char __user * buf = (char __user *)buffer; - int cnt = count/dma->inchannels; - int i; - - for (i=0;i < dma->inchannels;i++) { - char* hwbuf =(char*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=swptr; - - if (copy_to_user(buf,hwbuf,cnt)) - return -1; - buf+=hop; - } - swptr+=cnt; - break; - } - case AFMT_S16_LE: - { - int i,j; - int cnt = count/dma->inchannels; - for (i=0;i < dma->inchannels;i++) { - short __user * sbuf = (short __user *)buffer + i; - short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; - hwbuf+=(swptr>>1); - for (j=0;j<(cnt>>1);j++) { - hwbuf++; - __put_user(*hwbuf++,sbuf++); - sbuf+=(dma->inchannels-1); - } - } - swptr += (cnt<<1); - break; - } - default: - printk(RME_MESS" unsupported format\n"); - return -1; - } /* switch */ - - swptr&=((dma->s->fragsize<<1) -1); - dma->readptr = swptr; - return 0; -} - - -static irqreturn_t rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - int i; - rme96xx_info *s = (rme96xx_info *)dev_id; - struct dmabuf *db; - u32 status; - unsigned long flags; - - status = readl(s->iobase + RME96xx_status_register); - if (!(status & RME96xx_IRQ)) { - return IRQ_NONE; - } - - spin_lock_irqsave(&s->lock,flags); - writel(0,s->iobase + RME96xx_irq_clear); - - s->hwbufid = (status & RME96xx_buffer_id)>>26; - if ((status & 0xffc0) <= 256) s->hwptr = 0; - for(i=0;idma[i]); - if(db->started > 0) - wake_up(&(db->wait)); - } - spin_unlock_irqrestore(&s->lock,flags); - return IRQ_HANDLED; -} - - - -/*---------------------------------------------------------------------------- - PCI detection and module initialization stuff - ----------------------------------------------------------------------------*/ - -static void* busmaster_malloc(int size) { - int pg; /* 2 s exponent of memory size */ - char *buf; - - DBG(printk("kernel malloc pages ..\n")); - - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - - buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg); - - if (buf) { - struct page* page, *last_page; - - page = virt_to_page(buf); - last_page = page + (1 << pg); - DBG(printk("setting reserved bit\n")); - while (page < last_page) { - SetPageReserved(page); - page++; - } - return buf; - } - DBG(printk("allocated %ld",(long)buf)); - return NULL; -} - -static void busmaster_free(void* ptr,int size) { - int pg; - struct page* page, *last_page; - - if (ptr == NULL) - return; - - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - - page = virt_to_page(ptr); - last_page = page + (1 << pg); - while (page < last_page) { - ClearPageReserved(page); - page++; - } - DBG(printk("freeing pages\n")); - free_pages((unsigned long) ptr, pg); - DBG(printk("done\n")); -} - -/* initialize those parts of the info structure which are not pci detectable resources */ - -static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) { - - mutex_init(&dma->open_mutex); - init_waitqueue_head(&dma->open_wait); - init_waitqueue_head(&dma->wait); - dma->s = s; - dma->error = 0; - - dma->format = AFMT_S32_BLOCKED; - dma->formatshift = 0; - dma->inchannels = dma->outchannels = 1; - dma->inoffset = ioffset; - dma->outoffset = ooffset; - - dma->opened=0; - dma->started=0; - dma->mmapped=0; - dma->open_mode=0; - dma->mono=0; - - rme96xx_clearbufs(dma); - return 0; -} - - -static int rme96xx_init(rme96xx_info* s) -{ - int i; - int status; - unsigned short rev; - - DBG(printk("%s\n", __FUNCTION__)); - numcards++; - - s->magic = RME96xx_MAGIC; - - spin_lock_init(&s->lock); - - COMM ("setup busmaster memory") - s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); - s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); - - if (!s->recbuf || !s->playbuf) { - printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n"); - return -ENODEV; - } - - COMM ("setting rec and playbuffers") - - writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer); - writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer); - - COMM ("initializing control register") - rme96xx_unset_ctrl(s,0xffffffff); - rme96xx_set_ctrl(s,RME96xx_ctrl_init); - - - COMM ("setup devices") - for (i=0;i < devices;i++) { - struct dmabuf * dma = &s->dma[i]; - rme96xx_dmabuf_init(s,dma,2*i,2*i); - } - - /* code from ALSA card-rme9652.c HP 20020201 */ - /* Determine the h/w rev level of the card. This seems like - a particularly kludgy way to encode it, but its what RME - chose to do, so we follow them ... - */ - - status = readl(s->iobase + RME96xx_status_register); - if (rme96xx_decode_spdif_rate(status&RME96xx_F) == 1) { - s->hw_rev = 15; - } else { - s->hw_rev = 11; - } - - /* Differentiate between the standard Hammerfall, and the - "Light", which does not have the expansion board. This - method comes from information received from Mathhias - Clausen at RME. Display the EEPROM and h/w revID where - relevant. - */ - - pci_read_config_word(s->pcidev, PCI_CLASS_REVISION, &rev); - switch (rev & 0xff) { - case 8: /* original eprom */ - if (s->hw_rev == 15) { - s->card_name = "RME Digi9636 (Rev 1.5)"; - } else { - s->card_name = "RME Digi9636"; - } - break; - case 9: /* W36_G EPROM */ - s->card_name = "RME Digi9636 (Rev G)"; - break; - case 4: /* W52_G EPROM */ - s->card_name = "RME Digi9652 (Rev G)"; - break; - default: - case 3: /* original eprom */ - if (s->hw_rev == 15) { - s->card_name = "RME Digi9652 (Rev 1.5)"; - } else { - s->card_name = "RME Digi9652"; - } - break; - } - - printk(KERN_INFO RME_MESS" detected %s (hw_rev %d)\n",s->card_name,s->hw_rev); - - if (s->hw_rev == 15) - rme96xx_initialize_spdif_receiver (s); - - s->started = 0; - rme96xx_setlatency(s,7); - - printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); - return 0; -} - - -/* open uses this to figure out which device was opened .. this seems to be - unnecessary complex */ - -static LIST_HEAD(devs); - -static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - int i; - rme96xx_info *s; - - DBG(printk("%s\n", __FUNCTION__)); - - if (pcidev->irq == 0) - return -1; - if (!pci_dma_supported(pcidev, 0xffffffff)) { - printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n"); - return -1; - } - if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) { - printk(KERN_WARNING RME_MESS" out of memory\n"); - return -1; - } - memset(s, 0, sizeof(rme96xx_info)); - - s->pcidev = pcidev; - s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT); - s->irq = pcidev->irq; - - DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq)); - - if (pci_enable_device(pcidev)) - goto err_irq; - if (request_irq(s->irq, rme96xx_interrupt, IRQF_SHARED, "rme96xx", s)) { - printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); - goto err_irq; - } - - /* initialize the card */ - - i = 0; - if (rme96xx_init(s) < 0) { - printk(KERN_ERR RME_MESS" initialization failed\n"); - goto err_devices; - } - for (i=0;idspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0) - goto err_devices; - } - - if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0) - goto err_devices; - - pci_set_drvdata(pcidev, s); - pcidev->dma_mask = 0xffffffff; /* ????? */ - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - - DBG(printk("initialization successful\n")); - return 0; - - /* error handler */ - err_devices: - while (i--) - unregister_sound_dsp(s->dspnum[i]); - free_irq(s->irq,s); - err_irq: - kfree(s); - return -1; -} - - -static void __devexit rme96xx_remove(struct pci_dev *dev) -{ - int i; - rme96xx_info *s = pci_get_drvdata(dev); - - if (!s) { - printk(KERN_ERR"device structure not valid\n"); - return ; - } - - if (s->started) rme96xx_startcard(s,0); - - i = devices; - while (i) { - i--; - unregister_sound_dsp(s->dspnum[i]); - } - - unregister_sound_mixer(s->mixer); - synchronize_irq(s->irq); - free_irq(s->irq,s); - busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL); - busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL); - kfree(s); - pci_set_drvdata(dev, NULL); -} - - -#ifndef PCI_VENDOR_ID_RME -#define PCI_VENDOR_ID_RME 0x10ee -#endif -#ifndef PCI_DEVICE_ID_RME9652 -#define PCI_DEVICE_ID_RME9652 0x3fc4 -#endif -#ifndef PCI_ANY_ID -#define PCI_ANY_ID 0 -#endif - -static struct pci_device_id id_table[] = { - { - .vendor = PCI_VENDOR_ID_RME, - .device = PCI_DEVICE_ID_RME9652, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver rme96xx_driver = { - .name = "rme96xx", - .id_table = id_table, - .probe = rme96xx_probe, - .remove = __devexit_p(rme96xx_remove), -}; - -static int __init init_rme96xx(void) -{ - printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); - devices = ((devices-1) & RME96xx_MASK_DEVS) + 1; - printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); - numcards = 0; - return pci_register_driver(&rme96xx_driver); -} - -static void __exit cleanup_rme96xx(void) -{ - printk(KERN_INFO RME_MESS" unloading\n"); - pci_unregister_driver(&rme96xx_driver); -} - -module_init(init_rme96xx); -module_exit(cleanup_rme96xx); - - - - - -/*-------------------------------------------------------------------------- - Implementation of file operations ----------------------------------------------------------------------------*/ - -#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED) -/* AFTM_U8 is not (yet?) supported ... HP20020201 */ - -static int rme96xx_ioctl(struct inode *in, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct dmabuf * dma = (struct dmabuf *)file->private_data; - rme96xx_info *s = dma->s; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val = 0; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - - DBG(printk("ioctl %ud\n",cmd)); - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: -#if 0 - if (file->f_mode & FMODE_WRITE) - return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); -#endif - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: -// rme96xx_clearbufs(dma); - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { -/* generally it's not a problem if we change the speed - if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) - return -EINVAL; -*/ - spin_lock_irqsave(&s->lock, flags); - - switch (val) { - case 44100: - case 88200: - rme96xx_unset_ctrl(s,RME96xx_freq); - break; - case 48000: - case 96000: - rme96xx_set_ctrl(s,RME96xx_freq); - break; - /* just report current rate as default - e.g. use 0 to "select" current digital input rate - default: - rme96xx_unset_ctrl(s,RME96xx_freq); - val = 44100; - */ - } - if (val > 50000) - rme96xx_set_ctrl(s,RME96xx_DS); - else - rme96xx_unset_ctrl(s,RME96xx_DS); - /* set val to actual value HP 20020201 */ - /* NOTE: if not "Sync Master", reported rate might be not yet "updated" ... but I don't want to insert a long udelay() here */ - if ((s->control_register & RME96xx_Master) && !(s->control_register & RME96xx_wsel)) - val = rme96xx_get_sample_rate_ctrl(s); - else - val = rme96xx_get_sample_rate_status(s); - s->rate = val; - spin_unlock_irqrestore(&s->lock, flags); - } - DBG(printk("speed set to %d\n",val)); - return put_user(val, p); - - case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */ - if (get_user(val, p)) - return -EFAULT; - - if (!val) { - DBG(printk("setting to mono\n")); - dma->mono=1; - dma->inchannels = 1; - dma->outchannels = 1; - } - else { - DBG(printk("setting to stereo\n")); - dma->mono = 0; - dma->inchannels = 2; - dma->outchannels = 2; - } - return 0; - case SNDCTL_DSP_CHANNELS: - /* remember to check for resonable offset/channel pairs here */ - if (get_user(val, p)) - return -EFAULT; - - if (file->f_mode & FMODE_WRITE) { - if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) - dma->outchannels = val; - else - dma->outchannels = val = 2; - DBG(printk("setting to outchannels %d\n",val)); - } - if (file->f_mode & FMODE_READ) { - if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) - dma->inchannels = val; - else - dma->inchannels = val = 2; - DBG(printk("setting to inchannels %d\n",val)); - } - - dma->mono=0; - - return put_user(val, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(RME96xx_FMT, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - DBG(printk("setting to format %x\n",val)); - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (val & RME96xx_FMT) - dma->format = val; - switch (dma->format) { - case AFMT_S16_LE: - dma->formatshift=1; - break; - case AFMT_S32_BLOCKED: - dma->formatshift=0; - break; - } - } - return put_user(dma->format, p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; -#if 0 - if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) - val |= PCM_ENABLE_OUTPUT; -#endif - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; -#if 0 - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - return ret; - start_adc(s); - } else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) - return ret; - start_dac2(s); - } else - stop_dac2(s); - } -#endif - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - val = rme96xx_gethwptr(dma->s,0); - - - count = rme96xx_getospace(dma,val); - if (!s->started) count = s->fragsize*2; - abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift; - abinfo.bytes = (count*dma->outchannels)>>dma->formatshift; - abinfo.fragstotal = 2; - abinfo.fragments = (count > s->fragsize); - - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - - val = rme96xx_gethwptr(dma->s,0); - - count = rme96xx_getispace(dma,val); - - abinfo.fragsize = (s->fragsize*dma->inchannels)>>dma->formatshift; - abinfo.bytes = (count*dma->inchannels)>>dma->formatshift; - abinfo.fragstotal = 2; - abinfo.fragments = count > s->fragsize; - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: /* What should this exactly do ? , - ATM it is just abinfo.bytes */ - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - - val = rme96xx_gethwptr(dma->s,0); - count = val - dma->readptr; - if (count < 0) - count += s->fragsize<<1; - - return put_user(count, p); - - -/* check out how to use mmaped mode (can only be blocked !!!) */ - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - val = rme96xx_gethwptr(dma->s,0); - spin_lock_irqsave(&s->lock,flags); - cinfo.bytes = s->fragsize<<1; - count = val - dma->readptr; - if (count < 0) - count += s->fragsize<<1; - - cinfo.blocks = (count > s->fragsize); - cinfo.ptr = val; - if (dma->mmapped) - dma->readptr &= s->fragsize<<1; - spin_unlock_irqrestore(&s->lock,flags); - - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - val = rme96xx_gethwptr(dma->s,0); - spin_lock_irqsave(&s->lock,flags); - cinfo.bytes = s->fragsize<<1; - count = val - dma->writeptr; - if (count < 0) - count += s->fragsize<<1; - - cinfo.blocks = (count > s->fragsize); - cinfo.ptr = val; - if (dma->mmapped) - dma->writeptr &= s->fragsize<<1; - spin_unlock_irqrestore(&s->lock,flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - case SNDCTL_DSP_GETBLKSIZE: - return put_user(s->fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - val&=0xffff; - val -= 7; - if (val < 0) val = 0; - if (val > 7) val = 7; - rme96xx_setlatency(s,val); - return 0; - - case SNDCTL_DSP_SUBDIVIDE: -#if 0 - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac2.subdivision = val; -#endif - return 0; - - case SOUND_PCM_READ_RATE: - /* HP20020201 */ - s->rate = rme96xx_get_sample_rate_status(s); - return put_user(s->rate, p); - - case SOUND_PCM_READ_CHANNELS: - return put_user(dma->outchannels, p); - - case SOUND_PCM_READ_BITS: - switch (dma->format) { - case AFMT_S32_BLOCKED: - val = 32; - break; - case AFMT_S16_LE: - val = 16; - break; - } - return put_user(val, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - - - return -ENODEV; -} - - - -static int rme96xx_open(struct inode *in, struct file *f) -{ - int minor = iminor(in); - struct list_head *list; - int devnum; - rme96xx_info *s; - struct dmabuf* dma; - DECLARE_WAITQUEUE(wait, current); - - DBG(printk("device num %d open\n",devnum)); - - nonseekable_open(in, f); - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, rme96xx_info, devs); - for (devnum=0; devnumdspnum[devnum] ^ minor) & ~0xf)) - break; - if (devnumdma[devnum]; - f->private_data = dma; - /* wait for device to become free */ - mutex_lock(&dma->open_mutex); - while (dma->open_mode & f->f_mode) { - if (f->f_flags & O_NONBLOCK) { - mutex_unlock(&dma->open_mutex); - return -EBUSY; - } - add_wait_queue(&dma->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&dma->open_mutex); - schedule(); - remove_wait_queue(&dma->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&dma->open_mutex); - } - - COMM ("hardware open") - - if (!dma->opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset); - - dma->open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE)); - dma->opened = 1; - mutex_unlock(&dma->open_mutex); - - DBG(printk("device num %d open finished\n",devnum)); - return 0; -} - -static int rme96xx_release(struct inode *in, struct file *file) -{ - struct dmabuf * dma = (struct dmabuf*) file->private_data; - /* int hwp; ... was unused HP20020201 */ - DBG(printk("%s\n", __FUNCTION__)); - - COMM ("draining") - if (dma->open_mode & FMODE_WRITE) { -#if 0 /* Why doesn't this work with some cards ?? */ - hwp = rme96xx_gethwptr(dma->s,0); - while (rme96xx_getospace(dma,hwp)) { - interruptible_sleep_on(&(dma->wait)); - hwp = rme96xx_gethwptr(dma->s,0); - } -#endif - rme96xx_clearbufs(dma); - } - - dma->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - - if (!(dma->open_mode & (FMODE_READ|FMODE_WRITE))) { - dma->opened = 0; - if (dma->s->started) rme96xx_startcard(dma->s,1); - } - - wake_up(&dma->open_wait); - mutex_unlock(&dma->open_mutex); - - return 0; -} - - -static ssize_t rme96xx_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct dmabuf *dma = (struct dmabuf *)file->private_data; - ssize_t ret = 0; - int cnt; /* number of bytes from "buffer" that will/can be used */ - int hop = count/dma->outchannels; - int hwp; - int exact = (file->f_flags & O_NONBLOCK); - - - if(dma == NULL || (dma->s) == NULL) - return -ENXIO; - - if (dma->mmapped || !dma->opened) - return -ENXIO; - - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - - if (! (dma->open_mode & FMODE_WRITE)) - return -ENXIO; - - if (!dma->s->started) rme96xx_startcard(dma->s,exact); - hwp = rme96xx_gethwptr(dma->s,0); - - if(!(dma->started)){ - COMM ("first write") - - dma->readptr = hwp; - dma->writeptr = hwp; - dma->started = 1; - } - - while (count > 0) { - cnt = rme96xx_getospace(dma,hwp); - cnt>>=dma->formatshift; - cnt*=dma->outchannels; - if (cnt > count) - cnt = count; - - if (cnt != 0) { - if (rme96xx_copyfromuser(dma,buffer,cnt,hop)) - return ret ? ret : -EFAULT; - count -= cnt; - buffer += cnt; - ret += cnt; - if (count == 0) return ret; - } - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - - if ((hwp - dma->writeptr) <= 0) { - interruptible_sleep_on(&(dma->wait)); - - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - } - - hwp = rme96xx_gethwptr(dma->s,exact); - - }; /* count > 0 */ - - return ret; -} - -static ssize_t rme96xx_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct dmabuf *dma = (struct dmabuf *)file->private_data; - ssize_t ret = 0; - int cnt; /* number of bytes from "buffer" that will/can be used */ - int hop = count/dma->inchannels; - int hwp; - int exact = (file->f_flags & O_NONBLOCK); - - - if(dma == NULL || (dma->s) == NULL) - return -ENXIO; - - if (dma->mmapped || !dma->opened) - return -ENXIO; - - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - - if (! (dma->open_mode & FMODE_READ)) - return -ENXIO; - - if (!dma->s->started) rme96xx_startcard(dma->s,exact); - hwp = rme96xx_gethwptr(dma->s,0); - - if(!(dma->started)){ - COMM ("first read") - - dma->writeptr = hwp; - dma->readptr = hwp; - dma->started = 1; - } - - while (count > 0) { - cnt = rme96xx_getispace(dma,hwp); - cnt>>=dma->formatshift; - cnt*=dma->inchannels; - - if (cnt > count) - cnt = count; - - if (cnt != 0) { - - if (rme96xx_copytouser(dma,buffer,cnt,hop)) - return ret ? ret : -EFAULT; - - count -= cnt; - buffer += cnt; - ret += cnt; - if (count == 0) return ret; - } - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - - if ((hwp - dma->readptr) <= 0) { - interruptible_sleep_on(&(dma->wait)); - - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - } - hwp = rme96xx_gethwptr(dma->s,exact); - - }; /* count > 0 */ - - return ret; -} - -static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) { - struct dmabuf *dma = (struct dmabuf *)file->private_data; - rme96xx_info* s = dma->s; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - - if (vma->vm_pgoff != 0) { - unlock_kernel(); - return -EINVAL; - } - size = vma->vm_end - vma->vm_start; - if (size > RME96xx_DMA_MAX_SIZE) { - unlock_kernel(); - return -EINVAL; - } - - - if (vma->vm_flags & VM_WRITE) { - if (!s->started) rme96xx_startcard(s,1); - - if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->outoffset*RME96xx_DMA_MAX_SIZE) >> PAGE_SHIFT, size, vma->vm_page_prot)) { - unlock_kernel(); - return -EAGAIN; - } - } - else if (vma->vm_flags & VM_READ) { - if (!s->started) rme96xx_startcard(s,1); - if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->inoffset*RME96xx_DMA_MAX_SIZE) >> PAGE_SHIFT, size, vma->vm_page_prot)) { - unlock_kernel(); - return -EAGAIN; - } - } else { - unlock_kernel(); - return -EINVAL; - } - - -/* this is the mapping */ - vma->vm_flags &= ~VM_IO; - dma->mmapped = 1; - unlock_kernel(); - return 0; -} - -static unsigned int rme96xx_poll(struct file *file, struct poll_table_struct *wait) -{ - struct dmabuf *dma = (struct dmabuf *)file->private_data; - rme96xx_info* s = dma->s; - unsigned int mask = 0; - unsigned int hwp,cnt; - - DBG(printk("rme96xx poll_wait ...\n")); - VALIDATE_STATE(s); - - if (!s->started) { - mask |= POLLOUT | POLLWRNORM; - } - poll_wait(file, &dma->wait, wait); - - hwp = rme96xx_gethwptr(dma->s,0); - - DBG(printk("rme96xx poll: ..cnt %d > %d\n",cnt,s->fragsize)); - - cnt = rme96xx_getispace(dma,hwp); - - if (file->f_mode & FMODE_READ) - if (cnt > 0) - mask |= POLLIN | POLLRDNORM; - - - - cnt = rme96xx_getospace(dma,hwp); - - if (file->f_mode & FMODE_WRITE) - if (cnt > 0) - mask |= POLLOUT | POLLWRNORM; - - -// printk("rme96xx poll_wait ...%d > %d\n",rme96xx_getospace(dma,hwp),rme96xx_getispace(dma,hwp)); - - return mask; -} - - -static struct file_operations rme96xx_audio_fops = { - .owner = THIS_MODULE, - .read = rme96xx_read, - .write = rme96xx_write, - .poll = rme96xx_poll, - .ioctl = rme96xx_ioctl, - .mmap = rm96xx_mmap, - .open = rme96xx_open, - .release = rme96xx_release -}; - -static int rme96xx_mixer_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct list_head *list; - rme96xx_info *s; - - COMM ("mixer open"); - - nonseekable_open(inode, file); - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, rme96xx_info, devs); - if (s->mixer== minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - - COMM ("mixer opened") - return 0; -} - -static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - rme96xx_info *s = (rme96xx_info *)file->private_data; - u32 status; - int spdifrate; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - status = readl(s->iobase + RME96xx_status_register); - /* hack to convert rev 1.5 SPDIF rate to "crystalrate" format HP 20020201 */ - rme96xx_spdif_sample_rate(s,&spdifrate); - status = (status & ~RME96xx_F) | ((spdifrate<<22) & RME96xx_F); - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_PRIVATE1) { - rme_mixer mixer; - if (copy_from_user(&mixer,argp,sizeof(mixer))) - return -EFAULT; - - mixer.devnr &= RME96xx_MASK_DEVS; - if (mixer.devnr >= devices) - mixer.devnr = devices-1; - if (file->f_mode & FMODE_WRITE && !s->dma[mixer.devnr].opened) { - /* modify only if device not open */ - if (mixer.o_offset < 0) - mixer.o_offset = 0; - if (mixer.o_offset >= RME96xx_CHANNELS_PER_CARD) - mixer.o_offset = RME96xx_CHANNELS_PER_CARD-1; - if (mixer.i_offset < 0) - mixer.i_offset = 0; - if (mixer.i_offset >= RME96xx_CHANNELS_PER_CARD) - mixer.i_offset = RME96xx_CHANNELS_PER_CARD-1; - s->dma[mixer.devnr].outoffset = mixer.o_offset; - s->dma[mixer.devnr].inoffset = mixer.i_offset; - } - - mixer.o_offset = s->dma[mixer.devnr].outoffset; - mixer.i_offset = s->dma[mixer.devnr].inoffset; - - return copy_to_user(argp, &mixer, sizeof(mixer)) ? -EFAULT : 0; - } - if (cmd == SOUND_MIXER_PRIVATE2) { - return put_user(status, p); - } - if (cmd == SOUND_MIXER_PRIVATE3) { - u32 control; - if (copy_from_user(&control,argp,sizeof(control))) - return -EFAULT; - if (file->f_mode & FMODE_WRITE) { - s->control_register &= ~RME96xx_mixer_allowed; - s->control_register |= control & RME96xx_mixer_allowed; - writel(control,s->iobase + RME96xx_control_register); - } - - return put_user(s->control_register, p); - } - return -1; -} - - - -static int rme96xx_mixer_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static /*const*/ struct file_operations rme96xx_mixer_fops = { - .owner = THIS_MODULE, - .ioctl = rme96xx_mixer_ioctl, - .open = rme96xx_mixer_open, - .release = rme96xx_mixer_release, -}; diff --git a/sound/oss/rme96xx.h b/sound/oss/rme96xx.h deleted file mode 100644 index 7a3c188ea0..0000000000 --- a/sound/oss/rme96xx.h +++ /dev/null @@ -1,78 +0,0 @@ -/* (C) 2000 Guenter Geiger - with copy/pastes from the driver of Winfried Ritsch - -Modifications - Heiko Purnhagen - HP20020116 towards REV 1.5 support, based on ALSA's card-rme9652.c - HP20020201 completed? - -A text/graphic control panel (rmectrl/xrmectrl) is available from - http://gige.xdv.org/pages/soft/pages/rme -*/ - - -#ifndef AFMT_S32_BLOCKED -#define AFMT_S32_BLOCKED 0x0000400 -#endif - -/* AFMT_S16_BLOCKED not yet supported */ -#ifndef AFMT_S16_BLOCKED -#define AFMT_S16_BLOCKED 0x0000800 -#endif - - -typedef struct rme_status { - unsigned int irq:1; - unsigned int lockmask:3; /* ADAT input PLLs locked */ - /* 100=ADAT1, 010=ADAT2, 001=ADAT3 */ - unsigned int sr48:1; /* sample rate: 0=44.1/88.2 1=48/96 kHz */ - unsigned int wclock:1; /* 1=wordclock used */ - unsigned int bufpoint:10; - unsigned int syncmask:3; /* ADAT input in sync with system clock */ - /* 100=ADAT1, 010=ADAT2, 001=ADAT3 */ - unsigned int doublespeed:1; /* sample rate: 0=44.1/48 1=88.2/96 kHz */ - unsigned int tc_busy:1; - unsigned int tc_out:1; - unsigned int crystalrate:3; /* spdif input sample rate: */ - /* 000=64kHz, 100=88.2kHz, 011=96kHz */ - /* 111=32kHz, 110=44.1kHz, 101=48kHz */ - unsigned int spdif_error:1; /* 1=no spdif lock */ - unsigned int bufid:1; - unsigned int tc_valid:1; /* 1=timecode input detected */ - unsigned int spdif_read:1; -} rme_status_t; - - -/* only fields marked W: can be modified by writing to SOUND_MIXER_PRIVATE3 */ -typedef struct rme_control { - unsigned int start:1; - unsigned int latency:3; /* buffer size / latency [samples]: */ - /* 0=64 ... 7=8192 */ - unsigned int master:1; /* W: clock mode: 1=master 0=slave/auto */ - unsigned int ie:1; - unsigned int sr48:1; /* samplerate 0=44.1/88.2, 1=48/96 kHz */ - unsigned int spare:1; - unsigned int doublespeed:1; /* double speed 0=44.1/48, 1=88.2/96 Khz */ - unsigned int pro:1; /* W: SPDIF-OUT 0=consumer, 1=professional */ - unsigned int emphasis:1; /* W: SPDIF-OUT emphasis 0=off, 1=on */ - unsigned int dolby:1; /* W: SPDIF-OUT non-audio bit 1=set, 0=unset */ - unsigned int opt_out:1; /* W: use 1st optical OUT as SPDIF: 1=yes, 0=no */ - unsigned int wordclock:1; /* W: use Wordclock as sync (overwrites master) */ - unsigned int spdif_in:2; /* W: SPDIF-IN: */ - /* 00=optical (ADAT1), 01=coaxial (Cinch), 10=internal CDROM */ - unsigned int sync_ref:2; /* W: preferred sync-source in autosync */ - /* 00=ADAT1, 01=ADAT2, 10=ADAT3, 11=SPDIF */ - unsigned int spdif_reset:1; - unsigned int spdif_select:1; - unsigned int spdif_clock:1; - unsigned int spdif_write:1; - unsigned int adat1_cd:1; /* W: Rev 1.5+: if set, internal CD connector carries ADAT */ -} rme_ctrl_t; - - -typedef struct _rme_mixer { - int i_offset; - int o_offset; - int devnr; - int spare[8]; -} rme_mixer; - diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c index 0ce4e4ef6f..5c215f787c 100644 --- a/sound/oss/sequencer.c +++ b/sound/oss/sequencer.c @@ -16,7 +16,6 @@ */ #include #include -#define SEQUENCER_C #include "sound_config.h" #include "midi_ctrl.h" @@ -157,6 +156,7 @@ void seq_copy_to_input(unsigned char *event_rec, int len) wake_up(&midi_sleeper); spin_unlock_irqrestore(&lock,flags); } +EXPORT_SYMBOL(seq_copy_to_input); static void sequencer_midi_input(int dev, unsigned char data) { @@ -206,6 +206,7 @@ void seq_input_event(unsigned char *event_rec, int len) } seq_copy_to_input(event_rec, len); } +EXPORT_SYMBOL(seq_input_event); int sequencer_write(int dev, struct file *file, const char __user *buf, int count) { @@ -1554,6 +1555,7 @@ void sequencer_timer(unsigned long dummy) { seq_startplay(); } +EXPORT_SYMBOL(sequencer_timer); int note_to_freq(int note_num) { @@ -1587,6 +1589,7 @@ int note_to_freq(int note_num) return note_freq; } +EXPORT_SYMBOL(note_to_freq); unsigned long compute_finetune(unsigned long base_freq, int bend, int range, int vibrato_cents) @@ -1640,19 +1643,12 @@ unsigned long compute_finetune(unsigned long base_freq, int bend, int range, else return (base_freq * amount) / 10000; /* Bend up */ } - +EXPORT_SYMBOL(compute_finetune); void sequencer_init(void) { - /* drag in sequencer_syms.o */ - { - extern char sequencer_syms_symbol; - sequencer_syms_symbol = 0; - } - if (sequencer_ok) return; - MIDIbuf_init(); queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ); if (queue == NULL) { @@ -1668,6 +1664,7 @@ void sequencer_init(void) } sequencer_ok = 1; } +EXPORT_SYMBOL(sequencer_init); void sequencer_unload(void) { diff --git a/sound/oss/sequencer_syms.c b/sound/oss/sequencer_syms.c deleted file mode 100644 index 5d008798c3..0000000000 --- a/sound/oss/sequencer_syms.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Exported symbols for sequencer driver. - */ - -#include - -char sequencer_syms_symbol; - -#include "sound_config.h" -#include "sound_calls.h" - -EXPORT_SYMBOL(note_to_freq); -EXPORT_SYMBOL(compute_finetune); -EXPORT_SYMBOL(seq_copy_to_input); -EXPORT_SYMBOL(seq_input_event); -EXPORT_SYMBOL(sequencer_init); -EXPORT_SYMBOL(sequencer_timer); - -EXPORT_SYMBOL(sound_timer_init); -EXPORT_SYMBOL(sound_timer_interrupt); -EXPORT_SYMBOL(sound_timer_syncinterval); - -/* Tuning */ - -#define _SEQUENCER_C_ -#include "tuning.h" - -EXPORT_SYMBOL(cent_tuning); -EXPORT_SYMBOL(semitone_tuning); diff --git a/sound/oss/sgalaxy.c b/sound/oss/sgalaxy.c deleted file mode 100644 index 0bcff67353..0000000000 --- a/sound/oss/sgalaxy.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * sound/oss/sgalaxy.c - * - * Low level driver for Aztech Sound Galaxy cards. - * Copyright 1998 Artur Skawina - * - * Supported cards: - * Aztech Sound Galaxy Waverider Pro 32 - 3D - * Aztech Sound Galaxy Washington 16 - * - * Based on cs4232.c by Hannu Savolainen and Alan Cox. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added __init to sb_rst() and sb_cmd() - */ - -#include -#include - -#include "sound_config.h" -#include "ad1848.h" - -static void sleep( unsigned howlong ) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(howlong); -} - -#define DPORT 0x80 - -/* Sound Blaster regs */ - -#define SBDSP_RESET 0x6 -#define SBDSP_READ 0xA -#define SBDSP_COMMAND 0xC -#define SBDSP_STATUS SBDSP_COMMAND -#define SBDSP_DATA_AVAIL 0xE - -static int __init sb_rst(int base) -{ - int i; - - outb( 1, base+SBDSP_RESET ); /* reset the DSP */ - outb( 0, base+SBDSP_RESET ); - - for ( i=0; i<500; i++ ) /* delay */ - inb(DPORT); - - for ( i=0; i<100000; i++ ) - { - if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) - break; - } - - if ( inb( base+SBDSP_READ )!=0xAA ) - return 0; - - return 1; -} - -static int __init sb_cmd( int base, unsigned char val ) -{ - int i; - - for ( i=100000; i; i-- ) - { - if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) - { - outb( val, base+SBDSP_COMMAND ); - break; - } - } - return i; /* i>0 == success */ -} - - -#define ai_sgbase driver_use_1 - -static int __init probe_sgalaxy( struct address_info *ai ) -{ - struct resource *ports; - int n; - - if (!request_region(ai->io_base, 4, "WSS config")) { - printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); - return 0; - } - - ports = request_region(ai->io_base + 4, 4, "ad1848"); - if (!ports) { - printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); - release_region(ai->io_base, 4); - return 0; - } - - if (!request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB")) { - printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); - release_region(ai->io_base + 4, 4); - release_region(ai->io_base, 4); - return 0; - } - - if (ad1848_detect(ports, NULL, ai->osp)) - goto out; /* The card is already active, check irq etc... */ - - /* switch to MSS/WSS mode */ - - sb_rst( ai->ai_sgbase ); - - sb_cmd( ai->ai_sgbase, 9 ); - sb_cmd( ai->ai_sgbase, 0 ); - - sleep( HZ/10 ); - -out: - if (!probe_ms_sound(ai, ports)) { - release_region(ai->io_base + 4, 4); - release_region(ai->io_base, 4); - release_region(ai->ai_sgbase, 0x10); - return 0; - } - - attach_ms_sound(ai, ports, THIS_MODULE); - n=ai->slots[0]; - - if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) { - AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ - AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ - AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ - } - return 1; -} - -static void __exit unload_sgalaxy( struct address_info *ai ) -{ - unload_ms_sound( ai ); - release_region( ai->ai_sgbase, 0x10 ); -} - -static struct address_info cfg; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata sgbase = -1; - -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(dma, int, 0); -module_param(dma2, int, 0); -module_param(sgbase, int, 0); - -static int __init init_sgalaxy(void) -{ - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - cfg.ai_sgbase = sgbase; - - if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) { - printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); - return -EINVAL; - } - - if ( probe_sgalaxy(&cfg) == 0 ) - return -ENODEV; - - return 0; -} - -static void __exit cleanup_sgalaxy(void) -{ - unload_sgalaxy(&cfg); -} - -module_init(init_sgalaxy); -module_exit(cleanup_sgalaxy); - -#ifndef MODULE -static int __init setup_sgalaxy(char *str) -{ - /* io, irq, dma, dma2, sgbase */ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - sgbase = ints[5]; - - return 1; -} - -__setup("sgalaxy=", setup_sgalaxy); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c deleted file mode 100644 index 8ea532d401..0000000000 --- a/sound/oss/sonicvibes.c +++ /dev/null @@ -1,2792 +0,0 @@ -/*****************************************************************************/ - -/* - * sonicvibes.c -- S3 Sonic Vibes audio driver. - * - * Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) - * - * 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. - * - * Special thanks to David C. Niemi - * - * - * Module command line parameters: - * none so far - * - * - * Supported devices: - * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible - * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible - * /dev/midi simple MIDI UART interface, no ioctl - * - * The card has both an FM and a Wavetable synth, but I have to figure - * out first how to drive them... - * - * Revision history - * 06.05.1998 0.1 Initial release - * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation - * First stab at a simple midi interface (no bells&whistles) - * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of - * set_dac_rate in the FMODE_WRITE case in sv_open - * Fix hwptr out of bounds (now mpg123 works) - * 14.05.1998 0.4 Don't allow excessive interrupt rates - * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice - * 03.08.1998 0.6 Do not include modversions.h - * Now mixer behaviour can basically be selected between - * "OSS documented" and "OSS actual" behaviour - * 31.08.1998 0.7 Fix realplayer problems - dac.count issues - * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA - * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs - * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. - * hopefully killed the egcs section type conflict - * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. - * reported by Johan Maes - * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK - * read/write cannot be executed - * 05.04.1999 0.13 added code to sv_read and sv_write which should detect - * lockups of the sound chip and revive it. This is basically - * an ugly hack, but at least applications using this driver - * won't hang forever. I don't know why these lockups happen, - * it might well be the motherboard chipset (an early 486 PCI - * board with ALI chipset), since every busmastering 100MB - * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) - * exhibit similar behaviour (they work for a couple of packets - * and then lock up and can be revived by ifconfig down/up). - * 07.04.1999 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, - * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; - * Alpha fixes reported by Peter Jones - * Note: dmaio hack might still be wrong on archs other than i386 - * 15.06.1999 0.15 Fix bad allocation bug. - * Thanks to Deti Fliegl - * 28.06.1999 0.16 Add pci_set_master - * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall - * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" - * 12.08.1999 0.18 module_init/__setup fixes - * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource - * 31.08.1999 0.20 add spin_lock_init - * use new resource allocation to allocate DDMA IO space - * replaced current->state = x with set_current_state(x) - * 03.09.1999 0.21 change read semantics for MIDI to match - * OSS more closely; remove possible wakeup race - * 28.10.1999 0.22 More waitqueue races fixed - * 01.12.1999 0.23 New argument to allocate_resource - * 07.12.1999 0.24 More allocate_resource semantics change - * 08.01.2000 0.25 Prevent some ioctl's from returning bad count values on underrun/overrun; - * Tim Janik's BSE (Bedevilled Sound Engine) found this - * use Martin Mares' pci_assign_resource - * 07.02.2000 0.26 Use pci_alloc_consistent and pci_register_driver - * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask - * 12.12.2000 0.28 More dma buffer initializations, patch from - * Tjeerd Mulder - * 31.01.2001 0.29 Register/Unregister gameport - * Fix SETTRIGGER non OSS API conformity - * 18.05.2001 0.30 PCI probing and error values cleaned up by Marcus - * Meissner - * 03.01.2003 0.31 open_mode fixes from Georg Acher - * - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include - -#include "dm.h" - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -#define SUPPORT_JOYSTICK 1 -#endif - -/* --------------------------------------------------------------------- */ - -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -/* --------------------------------------------------------------------- */ - -#ifndef PCI_VENDOR_ID_S3 -#define PCI_VENDOR_ID_S3 0x5333 -#endif -#ifndef PCI_DEVICE_ID_S3_SONICVIBES -#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 -#endif - -#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) - -#define SV_EXTENT_SB 0x10 -#define SV_EXTENT_ENH 0x10 -#define SV_EXTENT_SYNTH 0x4 -#define SV_EXTENT_MIDI 0x4 -#define SV_EXTENT_GAME 0x8 -#define SV_EXTENT_DMA 0x10 - -/* - * we are not a bridge and thus use a resource for DDMA that is used for bridges but - * left empty for normal devices - */ -#define RESOURCE_SB 0 -#define RESOURCE_ENH 1 -#define RESOURCE_SYNTH 2 -#define RESOURCE_MIDI 3 -#define RESOURCE_GAME 4 -#define RESOURCE_DDMA 7 - -#define SV_MIDI_DATA 0 -#define SV_MIDI_COMMAND 1 -#define SV_MIDI_STATUS 1 - -#define SV_DMA_ADDR0 0 -#define SV_DMA_ADDR1 1 -#define SV_DMA_ADDR2 2 -#define SV_DMA_ADDR3 3 -#define SV_DMA_COUNT0 4 -#define SV_DMA_COUNT1 5 -#define SV_DMA_COUNT2 6 -#define SV_DMA_MODE 0xb -#define SV_DMA_RESET 0xd -#define SV_DMA_MASK 0xf - -/* - * DONT reset the DMA controllers unless you understand - * the reset semantics. Assuming reset semantics as in - * the 8237 does not work. - */ - -#define DMA_MODE_AUTOINIT 0x10 -#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ -#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ - -#define SV_CODEC_CONTROL 0 -#define SV_CODEC_INTMASK 1 -#define SV_CODEC_STATUS 2 -#define SV_CODEC_IADDR 4 -#define SV_CODEC_IDATA 5 - -#define SV_CCTRL_RESET 0x80 -#define SV_CCTRL_INTADRIVE 0x20 -#define SV_CCTRL_WAVETABLE 0x08 -#define SV_CCTRL_REVERB 0x04 -#define SV_CCTRL_ENHANCED 0x01 - -#define SV_CINTMASK_DMAA 0x01 -#define SV_CINTMASK_DMAC 0x04 -#define SV_CINTMASK_SPECIAL 0x08 -#define SV_CINTMASK_UPDOWN 0x40 -#define SV_CINTMASK_MIDI 0x80 - -#define SV_CSTAT_DMAA 0x01 -#define SV_CSTAT_DMAC 0x04 -#define SV_CSTAT_SPECIAL 0x08 -#define SV_CSTAT_UPDOWN 0x40 -#define SV_CSTAT_MIDI 0x80 - -#define SV_CIADDR_TRD 0x80 -#define SV_CIADDR_MCE 0x40 - -/* codec indirect registers */ -#define SV_CIMIX_ADCINL 0x00 -#define SV_CIMIX_ADCINR 0x01 -#define SV_CIMIX_AUX1INL 0x02 -#define SV_CIMIX_AUX1INR 0x03 -#define SV_CIMIX_CDINL 0x04 -#define SV_CIMIX_CDINR 0x05 -#define SV_CIMIX_LINEINL 0x06 -#define SV_CIMIX_LINEINR 0x07 -#define SV_CIMIX_MICIN 0x08 -#define SV_CIMIX_SYNTHINL 0x0A -#define SV_CIMIX_SYNTHINR 0x0B -#define SV_CIMIX_AUX2INL 0x0C -#define SV_CIMIX_AUX2INR 0x0D -#define SV_CIMIX_ANALOGINL 0x0E -#define SV_CIMIX_ANALOGINR 0x0F -#define SV_CIMIX_PCMINL 0x10 -#define SV_CIMIX_PCMINR 0x11 - -#define SV_CIGAMECONTROL 0x09 -#define SV_CIDATAFMT 0x12 -#define SV_CIENABLE 0x13 -#define SV_CIUPDOWN 0x14 -#define SV_CIREVISION 0x15 -#define SV_CIADCOUTPUT 0x16 -#define SV_CIDMAABASECOUNT1 0x18 -#define SV_CIDMAABASECOUNT0 0x19 -#define SV_CIDMACBASECOUNT1 0x1c -#define SV_CIDMACBASECOUNT0 0x1d -#define SV_CIPCMSR0 0x1e -#define SV_CIPCMSR1 0x1f -#define SV_CISYNTHSR0 0x20 -#define SV_CISYNTHSR1 0x21 -#define SV_CIADCCLKSOURCE 0x22 -#define SV_CIADCALTSR 0x23 -#define SV_CIADCPLLM 0x24 -#define SV_CIADCPLLN 0x25 -#define SV_CISYNTHPLLM 0x26 -#define SV_CISYNTHPLLN 0x27 -#define SV_CIUARTCONTROL 0x2a -#define SV_CIDRIVECONTROL 0x2b -#define SV_CISRSSPACE 0x2c -#define SV_CISRSCENTER 0x2d -#define SV_CIWAVETABLESRC 0x2e -#define SV_CIANALOGPWRDOWN 0x30 -#define SV_CIDIGITALPWRDOWN 0x31 - - -#define SV_CIMIX_ADCSRC_CD 0x20 -#define SV_CIMIX_ADCSRC_DAC 0x40 -#define SV_CIMIX_ADCSRC_AUX2 0x60 -#define SV_CIMIX_ADCSRC_LINE 0x80 -#define SV_CIMIX_ADCSRC_AUX1 0xa0 -#define SV_CIMIX_ADCSRC_MIC 0xc0 -#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 -#define SV_CIMIX_ADCSRC_MASK 0xe0 - -#define SV_CFMT_STEREO 0x01 -#define SV_CFMT_16BIT 0x02 -#define SV_CFMT_MASK 0x03 -#define SV_CFMT_ASHIFT 0 -#define SV_CFMT_CSHIFT 4 - -static const unsigned sample_size[] = { 1, 2, 2, 4 }; -static const unsigned sample_shift[] = { 0, 1, 1, 2 }; - -#define SV_CENABLE_PPE 0x4 -#define SV_CENABLE_RE 0x2 -#define SV_CENABLE_PE 0x1 - - -/* MIDI buffer sizes */ - -#define MIDIINBUF 256 -#define MIDIOUTBUF 256 - -#define FMODE_MIDI_SHIFT 2 -#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) -#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) - -#define FMODE_DMFM 0x10 - -/* --------------------------------------------------------------------- */ - -struct sv_state { - /* magic */ - unsigned int magic; - - /* list of sonicvibes devices */ - struct list_head devs; - - /* the corresponding pci_dev structure */ - struct pci_dev *dev; - - /* soundcore stuff */ - int dev_audio; - int dev_mixer; - int dev_midi; - int dev_dmfm; - - /* hardware resources */ - unsigned long iosb, ioenh, iosynth, iomidi; /* long for SPARC */ - unsigned int iodmaa, iodmac, irq; - - /* mixer stuff */ - struct { - unsigned int modcnt; -#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS - unsigned short vol[13]; -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - } mix; - - /* wave stuff */ - unsigned int rateadc, ratedac; - unsigned char fmt, enable; - - spinlock_t lock; - struct mutex open_mutex; - mode_t open_mode; - wait_queue_head_t open_wait; - - struct dmabuf { - void *rawbuf; - dma_addr_t dmaaddr; - unsigned buforder; - unsigned numfrag; - unsigned fragshift; - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - unsigned fragsamples; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned endcleared:1; - unsigned enabled:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; - } dma_dac, dma_adc; - - /* midi stuff */ - struct { - unsigned ird, iwr, icnt; - unsigned ord, owr, ocnt; - wait_queue_head_t iwait; - wait_queue_head_t owait; - struct timer_list timer; - unsigned char ibuf[MIDIINBUF]; - unsigned char obuf[MIDIOUTBUF]; - } midi; - -#if SUPPORT_JOYSTICK - struct gameport *gameport; -#endif -}; - -/* --------------------------------------------------------------------- */ - -static LIST_HEAD(devs); -static unsigned long wavetable_mem; - -/* --------------------------------------------------------------------- */ - -static inline unsigned ld2(unsigned int x) -{ - unsigned r = 0; - - if (x >= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -/* --------------------------------------------------------------------- */ - -/* - * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. - */ - -#undef DMABYTEIO - -static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmaa, u; - - count--; - for (u = 4; u > 0; u--, addr >>= 8, io++) - outb(addr & 0xff, io); - for (u = 3; u > 0; u--, count >>= 8, io++) - outb(count & 0xff, io); -#else /* DMABYTEIO */ - count--; - outl(addr, s->iodmaa + SV_DMA_ADDR0); - outl(count, s->iodmaa + SV_DMA_COUNT0); -#endif /* DMABYTEIO */ - outb(0x18, s->iodmaa + SV_DMA_MODE); -} - -static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmac, u; - - count >>= 1; - count--; - for (u = 4; u > 0; u--, addr >>= 8, io++) - outb(addr & 0xff, io); - for (u = 3; u > 0; u--, count >>= 8, io++) - outb(count & 0xff, io); -#else /* DMABYTEIO */ - count >>= 1; - count--; - outl(addr, s->iodmac + SV_DMA_ADDR0); - outl(count, s->iodmac + SV_DMA_COUNT0); -#endif /* DMABYTEIO */ - outb(0x14, s->iodmac + SV_DMA_MODE); -} - -static inline unsigned get_dmaa(struct sv_state *s) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmaa+6, v = 0, u; - - for (u = 3; u > 0; u--, io--) { - v <<= 8; - v |= inb(io); - } - return v + 1; -#else /* DMABYTEIO */ - return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; -#endif /* DMABYTEIO */ -} - -static inline unsigned get_dmac(struct sv_state *s) -{ -#ifdef DMABYTEIO - unsigned io = s->iodmac+6, v = 0, u; - - for (u = 3; u > 0; u--, io--) { - v <<= 8; - v |= inb(io); - } - return (v + 1) << 1; -#else /* DMABYTEIO */ - return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; -#endif /* DMABYTEIO */ -} - -static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) -{ - outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb(data, s->ioenh + SV_CODEC_IDATA); - udelay(10); -} - -static unsigned char rdindir(struct sv_state *s, unsigned char idx) -{ - unsigned char v; - - outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); - udelay(10); - v = inb(s->ioenh + SV_CODEC_IDATA); - udelay(10); - return v; -} - -static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); - if (mask) { - s->fmt = inb(s->ioenh + SV_CODEC_IDATA); - udelay(10); - } - s->fmt = (s->fmt & mask) | data; - outb(s->fmt, s->ioenh + SV_CODEC_IDATA); - udelay(10); - outb(0, s->ioenh + SV_CODEC_IADDR); - spin_unlock_irqrestore(&s->lock, flags); - udelay(10); -} - -static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) -{ - outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); - udelay(10); -} - -#define REFFREQUENCY 24576000 -#define ADCMULT 512 -#define FULLRATE 48000 - -static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) -{ - unsigned long flags; - unsigned char r, m=0, n=0; - unsigned xm, xn, xr, xd, metric = ~0U; - /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ - - if (rate < 625000/ADCMULT) - rate = 625000/ADCMULT; - if (rate > 150000000/ADCMULT) - rate = 150000000/ADCMULT; - /* slight violation of specs, needed for continuous sampling rates */ - for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); - for (xn = 3; xn < 35; xn++) - for (xm = 3; xm < 130; xm++) { - xr = REFFREQUENCY/ADCMULT * xm / xn; - xd = abs((signed)(xr - rate)); - if (xd < metric) { - metric = xd; - m = xm - 2; - n = xn - 2; - } - } - reg &= 0x3f; - spin_lock_irqsave(&s->lock, flags); - outb(reg, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb(m, s->ioenh + SV_CODEC_IDATA); - udelay(10); - outb(reg+1, s->ioenh + SV_CODEC_IADDR); - udelay(10); - outb(r | n, s->ioenh + SV_CODEC_IDATA); - spin_unlock_irqrestore(&s->lock, flags); - udelay(10); - return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); -} - -#if 0 - -static unsigned getpll(struct sv_state *s, unsigned char reg) -{ - unsigned long flags; - unsigned char m, n; - - reg &= 0x3f; - spin_lock_irqsave(&s->lock, flags); - outb(reg, s->ioenh + SV_CODEC_IADDR); - udelay(10); - m = inb(s->ioenh + SV_CODEC_IDATA); - udelay(10); - outb(reg+1, s->ioenh + SV_CODEC_IADDR); - udelay(10); - n = inb(s->ioenh + SV_CODEC_IDATA); - spin_unlock_irqrestore(&s->lock, flags); - udelay(10); - return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); -} - -#endif - -static void set_dac_rate(struct sv_state *s, unsigned rate) -{ - unsigned div; - unsigned long flags; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - div = (rate * 65536 + FULLRATE/2) / FULLRATE; - if (div > 65535) - div = 65535; - spin_lock_irqsave(&s->lock, flags); - wrindir(s, SV_CIPCMSR1, div >> 8); - wrindir(s, SV_CIPCMSR0, div); - spin_unlock_irqrestore(&s->lock, flags); - s->ratedac = (div * FULLRATE + 32768) / 65536; -} - -static void set_adc_rate(struct sv_state *s, unsigned rate) -{ - unsigned long flags; - unsigned rate1, rate2, div; - - if (rate > 48000) - rate = 48000; - if (rate < 4000) - rate = 4000; - rate1 = setpll(s, SV_CIADCPLLM, rate); - div = (48000 + rate/2) / rate; - if (div > 8) - div = 8; - rate2 = (48000 + div/2) / div; - spin_lock_irqsave(&s->lock, flags); - wrindir(s, SV_CIADCALTSR, (div-1) << 4); - if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { - wrindir(s, SV_CIADCCLKSOURCE, 0x10); - s->rateadc = rate2; - } else { - wrindir(s, SV_CIADCCLKSOURCE, 0x00); - s->rateadc = rate1; - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -static inline void stop_adc(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->enable &= ~SV_CENABLE_RE; - wrindir(s, SV_CIENABLE, s->enable); - spin_unlock_irqrestore(&s->lock, flags); -} - -static inline void stop_dac(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); - wrindir(s, SV_CIENABLE, s->enable); - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_dac(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { - s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; - wrindir(s, SV_CIENABLE, s->enable); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -static void start_adc(struct sv_state *s) -{ - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->enable |= SV_CENABLE_RE; - wrindir(s, SV_CIENABLE, s->enable); - } - spin_unlock_irqrestore(&s->lock, flags); -} - -/* --------------------------------------------------------------------- */ - -#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -static void dealloc_dmabuf(struct sv_state *s, struct dmabuf *db) -{ - struct page *page, *pend; - - if (db->rawbuf) { - /* undo marking the pages as reserved */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - ClearPageReserved(page); - pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); - } - db->rawbuf = NULL; - db->mapped = db->ready = 0; -} - - -/* DMAA is used for playback, DMAC is used for recording */ - -static int prog_dmabuf(struct sv_state *s, unsigned rec) -{ - struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; - unsigned rate = rec ? s->rateadc : s->ratedac; - int order; - unsigned bytepersec; - unsigned bufs; - struct page *page, *pend; - unsigned char fmt; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - fmt = s->fmt; - if (rec) { - s->enable &= ~SV_CENABLE_RE; - fmt >>= SV_CFMT_CSHIFT; - } else { - s->enable &= ~SV_CENABLE_PE; - fmt >>= SV_CFMT_ASHIFT; - } - wrindir(s, SV_CIENABLE, s->enable); - spin_unlock_irqrestore(&s->lock, flags); - fmt &= SV_CFMT_MASK; - db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; - if (!db->rawbuf) { - db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) - if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) - break; - if (!db->rawbuf) - return -ENOMEM; - db->buforder = order; - if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) - printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", - virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); - if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) - printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", - virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (page = virt_to_page(db->rawbuf); page <= pend; page++) - SetPageReserved(page); - } - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->fragshift; - memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); - spin_lock_irqsave(&s->lock, flags); - if (rec) { - set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); - wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); - } else { - set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); - wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); - } - spin_unlock_irqrestore(&s->lock, flags); - db->enabled = 1; - db->ready = 1; - return 0; -} - -static inline void clear_advance(struct sv_state *s) -{ - unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - bptr = 0; - len -= x; - } - memset(buf + bptr, c, len); -} - -/* call with spinlock held! */ -static void sv_update_ptr(struct sv_state *s) -{ - unsigned hwptr; - int diff; - - /* update ADC pointer */ - if (s->dma_adc.ready) { - hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->enable &= ~SV_CENABLE_RE; - wrindir(s, SV_CIENABLE, s->enable); - s->dma_adc.error++; - } - } - } - /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - s->enable &= ~SV_CENABLE_PE; - wrindir(s, SV_CIENABLE, s->enable); - s->dma_dac.error++; - } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; - } - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); - } - } -} - -/* hold spinlock for the following! */ -static void sv_handle_midi(struct sv_state *s) -{ - unsigned char ch; - int wake; - - wake = 0; - while (!(inb(s->iomidi+1) & 0x80)) { - ch = inb(s->iomidi); - if (s->midi.icnt < MIDIINBUF) { - s->midi.ibuf[s->midi.iwr] = ch; - s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; - s->midi.icnt++; - } - wake = 1; - } - if (wake) - wake_up(&s->midi.iwait); - wake = 0; - while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { - outb(s->midi.obuf[s->midi.ord], s->iomidi); - s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; - s->midi.ocnt--; - if (s->midi.ocnt < MIDIOUTBUF-16) - wake = 1; - } - if (wake) - wake_up(&s->midi.owait); -} - -static irqreturn_t sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct sv_state *s = (struct sv_state *)dev_id; - unsigned int intsrc; - - /* fastpath out, to ease interrupt sharing */ - intsrc = inb(s->ioenh + SV_CODEC_STATUS); - if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) - return IRQ_NONE; - spin_lock(&s->lock); - sv_update_ptr(s); - sv_handle_midi(s); - spin_unlock(&s->lock); - return IRQ_HANDLED; -} - -static void sv_midi_timer(unsigned long data) -{ - struct sv_state *s = (struct sv_state *)data; - unsigned long flags; - - spin_lock_irqsave(&s->lock, flags); - sv_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - s->midi.timer.expires = jiffies+1; - add_timer(&s->midi.timer); -} - -/* --------------------------------------------------------------------- */ - -static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != SV_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* --------------------------------------------------------------------- */ - -#define MT_4 1 -#define MT_5MUTE 2 -#define MT_4MUTEMONO 3 -#define MT_6MUTE 4 - -static const struct { - unsigned left:5; - unsigned right:5; - unsigned type:3; - unsigned rec:3; -} mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, - [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, - [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, - [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, - [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, - [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, - [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, - [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, - [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } -}; - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - -static int return_mixval(struct sv_state *s, unsigned i, int *arg) -{ - unsigned long flags; - unsigned char l, r, rl, rr; - - spin_lock_irqsave(&s->lock, flags); - l = rdindir(s, mixtable[i].left); - r = rdindir(s, mixtable[i].right); - spin_unlock_irqrestore(&s->lock, flags); - switch (mixtable[i].type) { - case MT_4: - r &= 0xf; - l &= 0xf; - rl = 10 + 6 * (l & 15); - rr = 10 + 6 * (r & 15); - break; - - case MT_4MUTEMONO: - rl = 55 - 3 * (l & 15); - if (r & 0x10) - rl += 45; - rr = rl; - r = l; - break; - - case MT_5MUTE: - default: - rl = 100 - 3 * (l & 31); - rr = 100 - 3 * (r & 31); - break; - - case MT_6MUTE: - rl = 100 - 3 * (l & 63) / 2; - rr = 100 - 3 * (r & 63) / 2; - break; - } - if (l & 0x80) - rl = 0; - if (r & 0x80) - rr = 0; - return put_user((rr << 8) | rl, arg); -} - -#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - -static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = -{ - [SOUND_MIXER_RECLEV] = 1, - [SOUND_MIXER_LINE1] = 2, - [SOUND_MIXER_CD] = 3, - [SOUND_MIXER_LINE] = 4, - [SOUND_MIXER_MIC] = 5, - [SOUND_MIXER_SYNTH] = 6, - [SOUND_MIXER_LINE2] = 7, - [SOUND_MIXER_VOLUME] = 8, - [SOUND_MIXER_PCM] = 9 -}; - -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - -static unsigned mixer_recmask(struct sv_state *s) -{ - unsigned long flags; - int i, j; - - spin_lock_irqsave(&s->lock, flags); - j = rdindir(s, SV_CIMIX_ADCINL) >> 5; - spin_unlock_irqrestore(&s->lock, flags); - j &= 7; - for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); - return 1 << i; -} - -static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val; - unsigned char l, r, rl, rr; - int __user *p = (int __user *)arg; - - VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, "SonicVibes", sizeof(info.id)); - strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); - info.modify_counter = s->mix.modcnt; - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, "SonicVibes", sizeof(info.id)); - strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - if (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ - if (get_user(val, p)) - return -EFAULT; - spin_lock_irqsave(&s->lock, flags); - if (val & 1) { - if (val & 2) { - l = 4 - ((val >> 2) & 7); - if (l & ~3) - l = 4; - r = 4 - ((val >> 5) & 7); - if (r & ~3) - r = 4; - wrindir(s, SV_CISRSSPACE, l); - wrindir(s, SV_CISRSCENTER, r); - } else - wrindir(s, SV_CISRSSPACE, 0x80); - } - l = rdindir(s, SV_CISRSSPACE); - r = rdindir(s, SV_CISRSCENTER); - spin_unlock_irqrestore(&s->lock, flags); - if (l & 0x80) - return put_user(0, p); - return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, p); - } - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(mixer_recmask(s), p); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].rec) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) - val |= 1 << i; - return put_user(val, p); - - case SOUND_MIXER_CAPS: - return put_user(SOUND_CAP_EXCL_INPUT, p); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - return return_mixval(s, i, p); -#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - if (!volidx[i]) - return -EINVAL; - return put_user(s->mix.vol[volidx[i]-1], p); -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - } - } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) - return -EINVAL; - s->mix.modcnt++; - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, p)) - return -EFAULT; - i = hweight32(val); - if (i == 0) - return 0; /*val = mixer_recmask(s);*/ - else if (i > 1) - val &= ~mixer_recmask(s); - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (mixtable[i].rec) - break; - } - if (i == SOUND_MIXER_NRDEVICES) - return 0; - spin_lock_irqsave(&s->lock, flags); - frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); - frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); - spin_unlock_irqrestore(&s->lock, flags); - return 0; - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - l = val & 0xff; - r = (val >> 8) & 0xff; - if (mixtable[i].type == MT_4MUTEMONO) - l = (r + l) / 2; - if (l > 100) - l = 100; - if (r > 100) - r = 100; - spin_lock_irqsave(&s->lock, flags); - switch (mixtable[i].type) { - case MT_4: - if (l >= 10) - l -= 10; - if (r >= 10) - r -= 10; - frobindir(s, mixtable[i].left, 0xf0, l / 6); - frobindir(s, mixtable[i].right, 0xf0, l / 6); - break; - - case MT_4MUTEMONO: - rr = 0; - if (l < 10) - rl = 0x80; - else { - if (l >= 55) { - rr = 0x10; - l -= 45; - } - rl = (55 - l) / 3; - } - wrindir(s, mixtable[i].left, rl); - frobindir(s, mixtable[i].right, ~0x10, rr); - break; - - case MT_5MUTE: - if (l < 7) - rl = 0x80; - else - rl = (100 - l) / 3; - if (r < 7) - rr = 0x80; - else - rr = (100 - r) / 3; - wrindir(s, mixtable[i].left, rl); - wrindir(s, mixtable[i].right, rr); - break; - - case MT_6MUTE: - if (l < 6) - rl = 0x80; - else - rl = (100 - l) * 2 / 3; - if (r < 6) - rr = 0x80; - else - rr = (100 - r) * 2 / 3; - wrindir(s, mixtable[i].left, rl); - wrindir(s, mixtable[i].right, rr); - break; - } - spin_unlock_irqrestore(&s->lock, flags); -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - return return_mixval(s, i, p); -#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - if (!volidx[i]) - return -EINVAL; - s->mix.vol[volidx[i]-1] = val; - return put_user(s->mix.vol[volidx[i]-1], p); -#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ - } -} - -/* --------------------------------------------------------------------- */ - -static int sv_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (s->dev_mixer == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - return nonseekable_open(inode, file); -} - -static int sv_release_mixdev(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - - VALIDATE_STATE(s); - return 0; -} - -static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); -} - -static /*const*/ struct file_operations sv_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = sv_ioctl_mixdev, - .open = sv_open_mixdev, - .release = sv_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_dac(struct sv_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; - tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; - if (!schedule_timeout(tmo + 1)) - printk(KERN_DEBUG "sv: dma timed out??\n"); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t sv_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; -#if 0 - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - spin_unlock_irqrestore(&s->lock, flags); -#endif - add_wait_queue(&s->dma_adc.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - swptr = s->dma_adc.swptr; - cnt = s->dma_adc.dmasize-swptr; - if (s->dma_adc.count < cnt) - cnt = s->dma_adc.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_adc.enabled) - start_adc(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (!schedule_timeout(HZ)) { - printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, - s->dma_adc.hwptr, s->dma_adc.swptr); - stop_adc(s); - spin_lock_irqsave(&s->lock, flags); - set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); - wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); - s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_adc.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_adc.swptr = swptr; - s->dma_adc.count -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_adc.enabled) - start_adc(s); - } - remove_wait_queue(&s->dma_adc.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -static ssize_t sv_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned swptr; - int cnt; - - VALIDATE_STATE(s); - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; -#if 0 - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - spin_unlock_irqrestore(&s->lock, flags); -#endif - add_wait_queue(&s->dma_dac.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - if (s->dma_dac.count < 0) { - s->dma_dac.count = 0; - s->dma_dac.swptr = s->dma_dac.hwptr; - } - swptr = s->dma_dac.swptr; - cnt = s->dma_dac.dmasize-swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (s->dma_dac.enabled) - start_dac(s); - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (!schedule_timeout(HZ)) { - printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, - s->dma_dac.hwptr, s->dma_dac.swptr); - stop_dac(s); - spin_lock_irqsave(&s->lock, flags); - set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); - /* program enhanced mode registers */ - wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); - wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); - s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; - spin_unlock_irqrestore(&s->lock, flags); - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - swptr = (swptr + cnt) % s->dma_dac.dmasize; - spin_lock_irqsave(&s->lock, flags); - s->dma_dac.swptr = swptr; - s->dma_dac.count += cnt; - s->dma_dac.endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (s->dma_dac.enabled) - start_dac(s); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - set_current_state(TASK_RUNNING); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf(s, 1)) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf(s, 0)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - if (file->f_mode & FMODE_READ) { - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int sv_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - unsigned long size; - - VALIDATE_STATE(s); - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 1)) != 0) - goto out; - db = &s->dma_dac; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 0)) != 0) - goto out; - db = &s->dma_adc; - } else - goto out; - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) - goto out; - ret = -EAGAIN; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(db->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - goto out; - db->mapped = 1; - ret = 0; -out: - unlock_kernel(); - return ret; -} - -static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int count; - int val, mapped, ret; - unsigned char fmtm, fmtd; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - set_adc_rate(s, val); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - set_dac_rate(s, val); - } - } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val) - fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val) - fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); - } - set_fmt(s, fmtm, fmtd); - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; - else - fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) - : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; - else - fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) - fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; - else - fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); - } - set_fmt(s, fmtm, fmtd); - } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) - : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, p); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, p); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; - s->dma_adc.enabled = 1; - start_adc(s); - } else { - s->dma_adc.enabled = 0; - stop_adc(s); - } - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; - s->dma_dac.enabled = 1; - start_dac(s); - } else { - s->dma_dac.enabled = 0; - stop_dac(s); - } - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - count = s->dma_dac.count; - if (count < 0) - count = 0; - abinfo.bytes = s->dma_dac.dmasize - count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - count = s->dma_adc.count; - if (count < 0) - count = 0; - abinfo.bytes = count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - if (count < 0) - count = 0; - return put_user(count, p); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - count = s->dma_adc.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->lock, flags); - sv_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - count = s->dma_dac.count; - if (count < 0) - count = 0; - cinfo.blocks = count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->lock, flags); - if (copy_to_user(argp, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) - return val; - return put_user(s->dma_dac.fragsize, p); - } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, p); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); - - case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) - : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, p); - - case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) - : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? 16 : 8, p); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - - } - return mixer_ioctl(s, cmd, arg); -} - -static int sv_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned char fmtm = ~0, fmts = 0; - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & file->f_mode) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - if (file->f_mode & FMODE_READ) { - fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; - s->dma_adc.enabled = 1; - set_adc_rate(s, 8000); - } - if (file->f_mode & FMODE_WRITE) { - fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; - s->dma_dac.enabled = 1; - set_dac_rate(s, 8000); - } - set_fmt(s, fmtm, fmts); - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int sv_release(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - - VALIDATE_STATE(s); - lock_kernel(); - if (file->f_mode & FMODE_WRITE) - drain_dac(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_mutex); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - dealloc_dmabuf(s, &s->dma_dac); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - } - s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations sv_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = sv_read, - .write = sv_write, - .poll = sv_poll, - .ioctl = sv_ioctl, - .mmap = sv_mmap, - .open = sv_open, - .release = sv_release, -}; - -/* --------------------------------------------------------------------- */ - -static ssize_t sv_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.iwait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.ird; - cnt = MIDIINBUF - ptr; - if (s->midi.icnt < cnt) - cnt = s->midi.icnt; - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIINBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.ird = ptr; - s->midi.icnt -= cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.iwait, &wait); - return ret; -} - -static ssize_t sv_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - unsigned long flags; - unsigned ptr; - int cnt; - - VALIDATE_STATE(s); - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - if (count == 0) - return 0; - ret = 0; - add_wait_queue(&s->midi.owait, &wait); - while (count > 0) { - spin_lock_irqsave(&s->lock, flags); - ptr = s->midi.owr; - cnt = MIDIOUTBUF - ptr; - if (s->midi.ocnt + cnt > MIDIOUTBUF) - cnt = MIDIOUTBUF - s->midi.ocnt; - if (cnt <= 0) { - __set_current_state(TASK_INTERRUPTIBLE); - sv_handle_midi(s); - } - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - ptr = (ptr + cnt) % MIDIOUTBUF; - spin_lock_irqsave(&s->lock, flags); - s->midi.owr = ptr; - s->midi.ocnt += cnt; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - spin_lock_irqsave(&s->lock, flags); - sv_handle_midi(s); - spin_unlock_irqrestore(&s->lock, flags); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&s->midi.owait, &wait); - return ret; -} - -/* No kernel lock - we have our own spinlock */ -static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &s->midi.owait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &s->midi.iwait, wait); - spin_lock_irqsave(&s->lock, flags); - if (file->f_mode & FMODE_READ) { - if (s->midi.icnt > 0) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (s->midi.ocnt < MIDIOUTBUF) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_irqrestore(&s->lock, flags); - return mask; -} - -static int sv_midi_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (s->dev_midi == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); - outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); - wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ - wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ - outb(0xff, s->iomidi+1); /* reset command */ - outb(0x3f, s->iomidi+1); /* uart command */ - if (!(inb(s->iomidi+1) & 0x80)) - inb(s->iomidi); - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - init_timer(&s->midi.timer); - s->midi.timer.expires = jiffies+1; - s->midi.timer.data = (unsigned long)s; - s->midi.timer.function = sv_midi_timer; - add_timer(&s->midi.timer); - } - if (file->f_mode & FMODE_READ) { - s->midi.ird = s->midi.iwr = s->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { - s->midi.ord = s->midi.owr = s->midi.ocnt = 0; - } - spin_unlock_irqrestore(&s->lock, flags); - s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int sv_midi_release(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned count, tmo; - - VALIDATE_STATE(s); - - lock_kernel(); - if (file->f_mode & FMODE_WRITE) { - add_wait_queue(&s->midi.owait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&s->lock, flags); - count = s->midi.ocnt; - spin_unlock_irqrestore(&s->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (file->f_flags & O_NONBLOCK) { - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - unlock_kernel(); - return -EBUSY; - } - tmo = (count * HZ) / 3100; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "sv: midi timed out??\n"); - } - remove_wait_queue(&s->midi.owait, &wait); - set_current_state(TASK_RUNNING); - } - mutex_lock(&s->open_mutex); - s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE)); - spin_lock_irqsave(&s->lock, flags); - if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { - outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); - del_timer(&s->midi.timer); - } - spin_unlock_irqrestore(&s->lock, flags); - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations sv_midi_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = sv_midi_read, - .write = sv_midi_write, - .poll = sv_midi_poll, - .open = sv_midi_open, - .release = sv_midi_release, -}; - -/* --------------------------------------------------------------------- */ - -static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - static const unsigned char op_offset[18] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 - }; - struct sv_state *s = (struct sv_state *)file->private_data; - struct dm_fm_voice v; - struct dm_fm_note n; - struct dm_fm_params p; - unsigned int io; - unsigned int regb; - - switch (cmd) { - case FM_IOCTL_RESET: - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->iosynth); - outb(0, s->iosynth+1); - outb(regb, s->iosynth+2); - outb(0, s->iosynth+3); - } - return 0; - - case FM_IOCTL_PLAY_NOTE: - if (copy_from_user(&n, (void __user *)arg, sizeof(n))) - return -EFAULT; - if (n.voice >= 18) - return -EINVAL; - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->iosynth+2; - } else { - regb = n.voice; - io = s->iosynth; - } - outb(0xa0 + regb, io); - outb(n.fnum & 0xff, io+1); - outb(0xb0 + regb, io); - outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); - return 0; - - case FM_IOCTL_SET_VOICE: - if (copy_from_user(&v, (void __user *)arg, sizeof(v))) - return -EFAULT; - if (v.voice >= 18) - return -EINVAL; - regb = op_offset[v.voice]; - io = s->iosynth + ((v.op & 1) << 1); - outb(0x20 + regb, io); - outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | - ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); - outb(0x40 + regb, io); - outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); - outb(0x60 + regb, io); - outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); - outb(0x80 + regb, io); - outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); - outb(0xe0 + regb, io); - outb(v.waveform & 0x7, io+1); - if (n.voice >= 9) { - regb = n.voice - 9; - io = s->iosynth+2; - } else { - regb = n.voice; - io = s->iosynth; - } - outb(0xc0 + regb, io); - outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | - (v.connection & 1), io+1); - return 0; - - case FM_IOCTL_SET_PARAMS: - if (copy_from_user(&p, (void *__user )arg, sizeof(p))) - return -EFAULT; - outb(0x08, s->iosynth); - outb((p.kbd_split & 1) << 6, s->iosynth+1); - outb(0xbd, s->iosynth); - outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | - ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); - return 0; - - case FM_IOCTL_SET_OPL: - outb(4, s->iosynth+2); - outb(arg, s->iosynth+3); - return 0; - - case FM_IOCTL_SET_MODE: - outb(5, s->iosynth+2); - outb(arg & 1, s->iosynth+3); - return 0; - - default: - return -EINVAL; - } -} - -static int sv_dmfm_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - struct list_head *list; - struct sv_state *s; - - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct sv_state, devs); - if (s->dev_dmfm == minor) - break; - } - VALIDATE_STATE(s); - file->private_data = s; - /* wait for device to become free */ - mutex_lock(&s->open_mutex); - while (s->open_mode & FMODE_DMFM) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_mutex); - return -EBUSY; - } - add_wait_queue(&s->open_wait, &wait); - __set_current_state(TASK_INTERRUPTIBLE); - mutex_unlock(&s->open_mutex); - schedule(); - remove_wait_queue(&s->open_wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - mutex_lock(&s->open_mutex); - } - /* init the stuff */ - outb(1, s->iosynth); - outb(0x20, s->iosynth+1); /* enable waveforms */ - outb(4, s->iosynth+2); - outb(0, s->iosynth+3); /* no 4op enabled */ - outb(5, s->iosynth+2); - outb(1, s->iosynth+3); /* enable OPL3 */ - s->open_mode |= FMODE_DMFM; - mutex_unlock(&s->open_mutex); - return nonseekable_open(inode, file); -} - -static int sv_dmfm_release(struct inode *inode, struct file *file) -{ - struct sv_state *s = (struct sv_state *)file->private_data; - unsigned int regb; - - VALIDATE_STATE(s); - lock_kernel(); - mutex_lock(&s->open_mutex); - s->open_mode &= ~FMODE_DMFM; - for (regb = 0xb0; regb < 0xb9; regb++) { - outb(regb, s->iosynth); - outb(0, s->iosynth+1); - outb(regb, s->iosynth+2); - outb(0, s->iosynth+3); - } - wake_up(&s->open_wait); - mutex_unlock(&s->open_mutex); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations sv_dmfm_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = sv_dmfm_ioctl, - .open = sv_dmfm_open, - .release = sv_dmfm_release, -}; - -/* --------------------------------------------------------------------- */ - -/* maximum number of devices; only used for command line params */ -#define NR_DEVICE 5 - -static int reverb[NR_DEVICE]; - -#if 0 -static int wavetable[NR_DEVICE]; -#endif - -static unsigned int devindex; - -module_param_array(reverb, bool, NULL, 0); -MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); -#if 0 -MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); -#endif - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("S3 SonicVibes Driver"); -MODULE_LICENSE("GPL"); - - -/* --------------------------------------------------------------------- */ - -static struct initvol { - int mixch; - int vol; -} initvol[] __devinitdata = { - { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, - { SOUND_MIXER_WRITE_LINE1, 0x4040 }, - { SOUND_MIXER_WRITE_CD, 0x4040 }, - { SOUND_MIXER_WRITE_LINE, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 }, - { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, - { SOUND_MIXER_WRITE_LINE2, 0x4040 }, - { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, - { SOUND_MIXER_WRITE_PCM, 0x4040 } -}; - -#define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \ - (pci_resource_flags((dev), (num)) & IORESOURCE_IO)) - -#ifdef SUPPORT_JOYSTICK -static int __devinit sv_register_gameport(struct sv_state *s, int io_port) -{ - struct gameport *gp; - - if (!request_region(io_port, SV_EXTENT_GAME, "S3 SonicVibes Gameport")) { - printk(KERN_ERR "sv: gameport io ports are in use\n"); - return -EBUSY; - } - - s->gameport = gp = gameport_allocate_port(); - if (!gp) { - printk(KERN_ERR "sv: can not allocate memory for gameport\n"); - release_region(io_port, SV_EXTENT_GAME); - return -ENOMEM; - } - - gameport_set_name(gp, "S3 SonicVibes Gameport"); - gameport_set_phys(gp, "isa%04x/gameport0", io_port); - gp->dev.parent = &s->dev->dev; - gp->io = io_port; - - gameport_register_port(gp); - - return 0; -} - -static inline void sv_unregister_gameport(struct sv_state *s) -{ - if (s->gameport) { - int gpio = s->gameport->io; - gameport_unregister_port(s->gameport); - release_region(gpio, SV_EXTENT_GAME); - } -} -#else -static inline int sv_register_gameport(struct sv_state *s, int io_port) { return -ENOSYS; } -static inline void sv_unregister_gameport(struct sv_state *s) { } -#endif /* SUPPORT_JOYSTICK */ - -static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) -{ - static char __devinitdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; - struct sv_state *s; - mm_segment_t fs; - int i, val, ret; - int gpio; - char *ddmaname; - unsigned ddmanamelen; - - if ((ret=pci_enable_device(pcidev))) - return ret; - - if (!RSRCISIOREGION(pcidev, RESOURCE_SB) || - !RSRCISIOREGION(pcidev, RESOURCE_ENH) || - !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) || - !RSRCISIOREGION(pcidev, RESOURCE_MIDI) || - !RSRCISIOREGION(pcidev, RESOURCE_GAME)) - return -ENODEV; - if (pcidev->irq == 0) - return -ENODEV; - if (pci_set_dma_mask(pcidev, DMA_24BIT_MASK)) { - printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n"); - return -ENODEV; - } - /* try to allocate a DDMA resource if not already available */ - if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) { - pcidev->resource[RESOURCE_DDMA].start = 0; - pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1; - pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO; - ddmanamelen = strlen(sv_ddma_name)+1; - if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL))) - return -1; - memcpy(ddmaname, sv_ddma_name, ddmanamelen); - pcidev->resource[RESOURCE_DDMA].name = ddmaname; - if (pci_assign_resource(pcidev, RESOURCE_DDMA)) { - pcidev->resource[RESOURCE_DDMA].name = NULL; - kfree(ddmaname); - printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n"); - return -EBUSY; - } - } - if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { - printk(KERN_WARNING "sv: out of memory\n"); - return -ENOMEM; - } - memset(s, 0, sizeof(struct sv_state)); - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->midi.iwait); - init_waitqueue_head(&s->midi.owait); - mutex_init(&s->open_mutex); - spin_lock_init(&s->lock); - s->magic = SV_MAGIC; - s->dev = pcidev; - s->iosb = pci_resource_start(pcidev, RESOURCE_SB); - s->ioenh = pci_resource_start(pcidev, RESOURCE_ENH); - s->iosynth = pci_resource_start(pcidev, RESOURCE_SYNTH); - s->iomidi = pci_resource_start(pcidev, RESOURCE_MIDI); - s->iodmaa = pci_resource_start(pcidev, RESOURCE_DDMA); - s->iodmac = pci_resource_start(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA; - gpio = pci_resource_start(pcidev, RESOURCE_GAME); - pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ - pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ - printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#x %#x %#x\n", - s->iosb, s->ioenh, s->iosynth, s->iomidi, gpio, s->iodmaa, s->iodmac); - s->irq = pcidev->irq; - - /* hack */ - pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ - - ret = -EBUSY; - if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) { - printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); - goto err_region5; - } - if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) { - printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); - goto err_region4; - } - if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) { - printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); - goto err_region3; - } - if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) { - printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); - goto err_region2; - } - if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) { - printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); - goto err_region1; - } - - /* initialize codec registers */ - outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ - udelay(50); - outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ - udelay(50); - outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */ - | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL); - inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ - wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ - wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ - outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); - /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ - /* outb(0xff, s->iodmac + SV_DMA_RESET); */ - inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ - wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ - wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ - wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ - setpll(s, SV_CIADCPLLM, 8000); - wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ - wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); - wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); - wrindir(s, SV_CIADCOUTPUT, 0); - /* request irq */ - if ((ret=request_irq(s->irq,sv_interrupt,IRQF_SHARED,"S3 SonicVibes",s))) { - printk(KERN_ERR "sv: irq %u in use\n", s->irq); - goto err_irq; - } - printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n", - s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); - /* register devices */ - if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) { - ret = s->dev_audio; - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) { - ret = s->dev_mixer; - goto err_dev2; - } - if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) { - ret = s->dev_midi; - goto err_dev3; - } - if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) { - ret = s->dev_dmfm; - goto err_dev4; - } - pci_set_master(pcidev); /* enable bus mastering */ - /* initialize the chips */ - fs = get_fs(); - set_fs(KERNEL_DS); - val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); - for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); - } - set_fs(fs); - /* register gameport */ - sv_register_gameport(s, gpio); - /* store it in the driver field */ - pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - /* increment devindex */ - if (devindex < NR_DEVICE-1) - devindex++; - return 0; - - err_dev4: - unregister_sound_midi(s->dev_midi); - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - printk(KERN_ERR "sv: cannot register misc device\n"); - free_irq(s->irq, s); - err_irq: - release_region(s->iosynth, SV_EXTENT_SYNTH); - err_region1: - release_region(s->iomidi, SV_EXTENT_MIDI); - err_region2: - release_region(s->iodmac, SV_EXTENT_DMA); - err_region3: - release_region(s->iodmaa, SV_EXTENT_DMA); - err_region4: - release_region(s->ioenh, SV_EXTENT_ENH); - err_region5: - kfree(s); - return ret; -} - -static void __devexit sv_remove(struct pci_dev *dev) -{ - struct sv_state *s = pci_get_drvdata(dev); - - if (!s) - return; - list_del(&s->devs); - outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ - synchronize_irq(s->irq); - inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ - wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ - /*outb(0, s->iodmaa + SV_DMA_RESET);*/ - /*outb(0, s->iodmac + SV_DMA_RESET);*/ - free_irq(s->irq, s); - sv_unregister_gameport(s); - release_region(s->iodmac, SV_EXTENT_DMA); - release_region(s->iodmaa, SV_EXTENT_DMA); - release_region(s->ioenh, SV_EXTENT_ENH); - release_region(s->iomidi, SV_EXTENT_MIDI); - release_region(s->iosynth, SV_EXTENT_SYNTH); - unregister_sound_dsp(s->dev_audio); - unregister_sound_mixer(s->dev_mixer); - unregister_sound_midi(s->dev_midi); - unregister_sound_special(s->dev_dmfm); - kfree(s); - pci_set_drvdata(dev, NULL); -} - -static struct pci_device_id id_table[] = { - { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, id_table); - -static struct pci_driver sv_driver = { - .name = "sonicvibes", - .id_table = id_table, - .probe = sv_probe, - .remove = __devexit_p(sv_remove), -}; - -static int __init init_sonicvibes(void) -{ - printk(KERN_INFO "sv: version v0.31 time " __TIME__ " " __DATE__ "\n"); -#if 0 - if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) - printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); -#endif - return pci_register_driver(&sv_driver); -} - -static void __exit cleanup_sonicvibes(void) -{ - printk(KERN_INFO "sv: unloading\n"); - pci_unregister_driver(&sv_driver); - if (wavetable_mem) - free_pages(wavetable_mem, 20-PAGE_SHIFT); -} - -module_init(init_sonicvibes); -module_exit(cleanup_sonicvibes); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */ - -static int __init sonicvibes_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= NR_DEVICE) - return 0; -#if 0 - if (get_option(&str, &reverb[nr_dev]) == 2) - (void)get_option(&str, &wavetable[nr_dev]); -#else - (void)get_option(&str, &reverb[nr_dev]); -#endif - - nr_dev++; - return 1; -} - -__setup("sonicvibes=", sonicvibes_setup); - -#endif /* MODULE */ diff --git a/sound/oss/sound_calls.h b/sound/oss/sound_calls.h index 1ae0750966..87d8ad4a03 100644 --- a/sound/oss/sound_calls.h +++ b/sound/oss/sound_calls.h @@ -13,8 +13,6 @@ int DMAbuf_move_wrpointer(int dev, int l); void DMAbuf_init(int dev, int dma1, int dma2); void DMAbuf_deinit(int dev); int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); -int DMAbuf_open_dma (int dev); -void DMAbuf_close_dma (int dev); void DMAbuf_inputintr(int dev); void DMAbuf_outputintr(int dev, int underflow_flag); struct dma_buffparms; @@ -73,7 +71,6 @@ unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait); int MIDIbuf_avail(int dev); void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); -void MIDIbuf_init(void); /* From soundcard.c */ diff --git a/sound/oss/sound_syms.c b/sound/oss/sound_syms.c deleted file mode 100644 index cb7c33fe5b..0000000000 --- a/sound/oss/sound_syms.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * The sound core exports the following symbols to the rest of - * modulespace. - * - * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL - * - * Thu May 27 1999 Andrew J. Kroll - * left out exported symbol... fixed - */ - -#include -#include "sound_config.h" -#include "sound_calls.h" - -char sound_syms_symbol; - -EXPORT_SYMBOL(mixer_devs); -EXPORT_SYMBOL(audio_devs); -EXPORT_SYMBOL(num_mixers); -EXPORT_SYMBOL(num_audiodevs); - -EXPORT_SYMBOL(midi_devs); -EXPORT_SYMBOL(num_midis); -EXPORT_SYMBOL(synth_devs); - -EXPORT_SYMBOL(sound_timer_devs); - -EXPORT_SYMBOL(sound_install_audiodrv); -EXPORT_SYMBOL(sound_install_mixer); -EXPORT_SYMBOL(sound_alloc_dma); -EXPORT_SYMBOL(sound_free_dma); -EXPORT_SYMBOL(sound_open_dma); -EXPORT_SYMBOL(sound_close_dma); -EXPORT_SYMBOL(sound_alloc_mididev); -EXPORT_SYMBOL(sound_alloc_mixerdev); -EXPORT_SYMBOL(sound_alloc_timerdev); -EXPORT_SYMBOL(sound_alloc_synthdev); -EXPORT_SYMBOL(sound_unload_audiodev); -EXPORT_SYMBOL(sound_unload_mididev); -EXPORT_SYMBOL(sound_unload_mixerdev); -EXPORT_SYMBOL(sound_unload_timerdev); -EXPORT_SYMBOL(sound_unload_synthdev); - -EXPORT_SYMBOL(load_mixer_volumes); - -EXPORT_SYMBOL(conf_printf); -EXPORT_SYMBOL(conf_printf2); - -MODULE_DESCRIPTION("OSS Sound subsystem"); -MODULE_AUTHOR("Hannu Savolainen, et al."); diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c index 146bf85de9..f0f0c19fbf 100644 --- a/sound/oss/sound_timer.c +++ b/sound/oss/sound_timer.c @@ -76,6 +76,7 @@ void sound_timer_syncinterval(unsigned int new_usecs) tmr_ctr = 0; usecs_per_tmr = new_usecs; } +EXPORT_SYMBOL(sound_timer_syncinterval); static void tmr_reset(void) { @@ -300,6 +301,7 @@ void sound_timer_interrupt(void) } spin_unlock_irqrestore(&lock,flags); } +EXPORT_SYMBOL(sound_timer_interrupt); void sound_timer_init(struct sound_lowlev_timer *t, char *name) { @@ -321,3 +323,5 @@ void sound_timer_init(struct sound_lowlev_timer *t, char *name) strcpy(sound_timer.info.name, name); sound_timer_devs[n] = &sound_timer; } +EXPORT_SYMBOL(sound_timer_init); + diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c index 683dc00a8d..2344d09c71 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -107,6 +107,7 @@ int *load_mixer_volumes(char *name, int *levels, int present) mixer_vols[n].levels[i] = levels[i]; return mixer_vols[n].levels; } +EXPORT_SYMBOL(load_mixer_volumes); static int set_mixer_levels(void __user * arg) { @@ -541,12 +542,6 @@ static int __init oss_init(void) int err; int i, j; - /* drag in sound_syms.o */ - { - extern char sound_syms_symbol; - sound_syms_symbol = 0; - } - #ifdef CONFIG_PCI if(dmabug) isa_dma_bridge_buggy = dmabug; @@ -614,6 +609,8 @@ static void __exit oss_cleanup(void) module_init(oss_init); module_exit(oss_cleanup); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("OSS Sound subsystem"); +MODULE_AUTHOR("Hannu Savolainen, et al."); int sound_alloc_dma(int chn, char *deviceID) @@ -627,6 +624,7 @@ int sound_alloc_dma(int chn, char *deviceID) return 0; } +EXPORT_SYMBOL(sound_alloc_dma); int sound_open_dma(int chn, char *deviceID) { @@ -642,6 +640,7 @@ int sound_open_dma(int chn, char *deviceID) dma_alloc_map[chn] = DMA_MAP_BUSY; return 0; } +EXPORT_SYMBOL(sound_open_dma); void sound_free_dma(int chn) { @@ -652,6 +651,7 @@ void sound_free_dma(int chn) free_dma(chn); dma_alloc_map[chn] = DMA_MAP_UNAVAIL; } +EXPORT_SYMBOL(sound_free_dma); void sound_close_dma(int chn) { @@ -661,6 +661,7 @@ void sound_close_dma(int chn) } dma_alloc_map[chn] = DMA_MAP_FREE; } +EXPORT_SYMBOL(sound_close_dma); static void do_sequencer_timer(unsigned long dummy) { @@ -714,6 +715,7 @@ void conf_printf(char *name, struct address_info *hw_config) printk("\n"); #endif } +EXPORT_SYMBOL(conf_printf); void conf_printf2(char *name, int base, int irq, int dma, int dma2) { @@ -734,3 +736,5 @@ void conf_printf2(char *name, int base, int irq, int dma, int dma2) printk("\n"); #endif } +EXPORT_SYMBOL(conf_printf2); + diff --git a/sound/oss/tuning.h b/sound/oss/tuning.h index 858e1fe6c6..a73e3dd39f 100644 --- a/sound/oss/tuning.h +++ b/sound/oss/tuning.h @@ -1,13 +1,11 @@ -#ifdef SEQUENCER_C - -unsigned short semitone_tuning[24] = +static unsigned short semitone_tuning[24] = { /* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, /* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, /* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 }; -unsigned short cent_tuning[100] = +static unsigned short cent_tuning[100] = { /* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, /* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, @@ -23,7 +21,3 @@ unsigned short cent_tuning[100] = /* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, /* 96 */ 10570, 10576, 10582, 10589 }; -#else -extern unsigned short semitone_tuning[24]; -extern unsigned short cent_tuning[100]; -#endif diff --git a/sound/oss/wavfront.c b/sound/oss/wavfront.c deleted file mode 100644 index 1dec3958cc..0000000000 --- a/sound/oss/wavfront.c +++ /dev/null @@ -1,3554 +0,0 @@ -/* -*- linux-c -*- - * - * sound/wavfront.c - * - * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) - * - * This driver supports the onboard wavetable synthesizer (an ICS2115), - * including patch, sample and program loading and unloading, conversion - * of GUS patches during loading, and full user-level access to all - * WaveFront commands. It tries to provide semi-intelligent patch and - * sample management as well. - * - * It also provides support for the ICS emulation of an MPU-401. Full - * support for the ICS emulation's "virtual MIDI mode" is provided in - * wf_midi.c. - * - * Support is also provided for the Tropez Plus' onboard FX processor, - * a Yamaha YSS225. Currently, code exists to configure the YSS225, - * and there is an interface allowing tweaking of any of its memory - * addresses. However, I have been unable to decipher the logical - * positioning of the configuration info for various effects, so for - * now, you just get the YSS225 in the same state as Turtle Beach's - * "SETUPSND.EXE" utility leaves it. - * - * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co], - * This chip also controls the configuration of the card: the wavefront - * synth is logical unit 4. - * - * - * Supported devices: - * - * /dev/dsp - using cs4232+ad1848 modules, OSS compatible - * /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible - * /dev/synth00 - raw synth interface - * - ********************************************************************** - * - * Copyright (C) by Paul Barton-Davis 1998 - * - * Some portions of this file are taken from work that is - * copyright (C) by Hannu Savolainen 1993-1996 - * - * Although the relevant code here is all new, the handling of - * sample/alias/multi- samples is entirely based on a driver by Matt - * Martin and Rutger Nijlunsing which demonstrated how to get things - * to work correctly. The GUS patch loading code has been almost - * unaltered by me, except to fit formatting and function names in the - * rest of the file. Many thanks to them. - * - * Appreciation and thanks to Hannu Savolainen for his early work on the Maui - * driver, and answering a few questions while this one was developed. - * - * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their - * complete lack of help in developing this driver, and in particular - * for their utter silence in response to questions about undocumented - * aspects of configuring a WaveFront soundcard, particularly the - * effects processor. - * - * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $ - * - * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz - * Added some __init and __initdata to entries in yss225.c - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "sound_config.h" - -#include - -#define _MIDI_SYNTH_C_ -#define MIDI_SYNTH_NAME "WaveFront MIDI" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -/* Compile-time control of the extent to which OSS is supported. - - I consider /dev/sequencer to be an anachronism, but given its - widespread usage by various Linux MIDI software, it seems worth - offering support to it if it's not too painful. Instead of using - /dev/sequencer, I recommend: - - for synth programming and patch loading: /dev/synthNN - for kernel-synchronized MIDI sequencing: the ALSA sequencer - for direct MIDI control: /dev/midiNN - - I have never tried static compilation into the kernel. The #if's - for this are really just notes to myself about what the code is - for. -*/ - -#define OSS_SUPPORT_SEQ 0x1 /* use of /dev/sequencer */ -#define OSS_SUPPORT_STATIC_INSTALL 0x2 /* static compilation into kernel */ - -#define OSS_SUPPORT_LEVEL 0x1 /* just /dev/sequencer for now */ - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ -static int (*midi_load_patch) (int devno, int format, const char __user *addr, - int offs, int count, int pmgr_flag) = NULL; -#endif /* OSS_SUPPORT_SEQ */ - -/* if WF_DEBUG not defined, no run-time debugging messages will - be available via the debug flag setting. Given the current - beta state of the driver, this will remain set until a future - version. -*/ - -#define WF_DEBUG 1 - -#ifdef WF_DEBUG - -/* Thank goodness for gcc's preprocessor ... */ - -#define DPRINT(cond, format, args...) \ - if ((dev.debug & (cond)) == (cond)) { \ - printk (KERN_DEBUG LOGNAME format, ## args); \ - } -#else -#define DPRINT(cond, format, args...) -#endif - -#define LOGNAME "WaveFront: " - -/* bitmasks for WaveFront status port value */ - -#define STAT_RINTR_ENABLED 0x01 -#define STAT_CAN_READ 0x02 -#define STAT_INTR_READ 0x04 -#define STAT_WINTR_ENABLED 0x10 -#define STAT_CAN_WRITE 0x20 -#define STAT_INTR_WRITE 0x40 - -/*** Module-accessible parameters ***************************************/ - -static int wf_raw; /* we normally check for "raw state" to firmware - loading. if set, then during driver loading, the - state of the board is ignored, and we reset the - board and load the firmware anyway. - */ - -static int fx_raw = 1; /* if this is zero, we'll leave the FX processor in - whatever state it is when the driver is loaded. - The default is to download the microprogram and - associated coefficients to set it up for "default" - operation, whatever that means. - */ - -static int debug_default; /* you can set this to control debugging - during driver loading. it takes any combination - of the WF_DEBUG_* flags defined in - wavefront.h - */ - -/* XXX this needs to be made firmware and hardware version dependent */ - -static char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed - version of the WaveFront OS - */ - -static int wait_polls = 2000; /* This is a number of tries we poll the - status register before resorting to sleeping. - WaveFront being an ISA card each poll takes - about 1.2us. So before going to - sleep we wait up to 2.4ms in a loop. - */ - -static int sleep_length = HZ/100; /* This says how long we're going to - sleep between polls. - 10ms sounds reasonable for fast response. - */ - -static int sleep_tries = 50; /* Wait for status 0.5 seconds total. */ - -static int reset_time = 2; /* hundreths of a second we wait after a HW reset for - the expected interrupt. - */ - -static int ramcheck_time = 20; /* time in seconds to wait while ROM code - checks on-board RAM. - */ - -static int osrun_time = 10; /* time in seconds we wait for the OS to - start running. - */ - -module_param(wf_raw, int, 0); -module_param(fx_raw, int, 0); -module_param(debug_default, int, 0); -module_param(wait_polls, int, 0); -module_param(sleep_length, int, 0); -module_param(sleep_tries, int, 0); -module_param(ospath, charp, 0); -module_param(reset_time, int, 0); -module_param(ramcheck_time, int, 0); -module_param(osrun_time, int, 0); - -/***************************************************************************/ - -/* Note: because this module doesn't export any symbols, this really isn't - a global variable, even if it looks like one. I was quite confused by - this when I started writing this as a (newer) module -- pbd. -*/ - -struct wf_config { - int devno; /* device number from kernel */ - int irq; /* "you were one, one of the few ..." */ - int base; /* low i/o port address */ - -#define mpu_data_port base -#define mpu_command_port base + 1 /* write semantics */ -#define mpu_status_port base + 1 /* read semantics */ -#define data_port base + 2 -#define status_port base + 3 /* read semantics */ -#define control_port base + 3 /* write semantics */ -#define block_port base + 4 /* 16 bit, writeonly */ -#define last_block_port base + 6 /* 16 bit, writeonly */ - - /* FX ports. These are mapped through the ICS2115 to the YS225. - The ICS2115 takes care of flipping the relevant pins on the - YS225 so that access to each of these ports does the right - thing. Note: these are NOT documented by Turtle Beach. - */ - -#define fx_status base + 8 -#define fx_op base + 8 -#define fx_lcr base + 9 -#define fx_dsp_addr base + 0xa -#define fx_dsp_page base + 0xb -#define fx_dsp_lsb base + 0xc -#define fx_dsp_msb base + 0xd -#define fx_mod_addr base + 0xe -#define fx_mod_data base + 0xf - - volatile int irq_ok; /* set by interrupt handler */ - volatile int irq_cnt; /* ditto */ - int opened; /* flag, holds open(2) mode */ - char debug; /* debugging flags */ - int freemem; /* installed RAM, in bytes */ - - int synth_dev; /* devno for "raw" synth */ - int mididev; /* devno for internal MIDI */ - int ext_mididev; /* devno for external MIDI */ - int fx_mididev; /* devno for FX MIDI interface */ -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - int oss_dev; /* devno for OSS sequencer synth */ -#endif /* OSS_SUPPORT_SEQ */ - - char fw_version[2]; /* major = [0], minor = [1] */ - char hw_version[2]; /* major = [0], minor = [1] */ - char israw; /* needs Motorola microcode */ - char has_fx; /* has FX processor (Tropez+) */ - char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ - char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ - char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ - int samples_used; /* how many */ - char interrupts_on; /* h/w MPU interrupts enabled ? */ - char rom_samples_rdonly; /* can we write on ROM samples */ - wait_queue_head_t interrupt_sleeper; -} dev; - -static DEFINE_SPINLOCK(lock); -static int detect_wffx(void); -static int wffx_ioctl (wavefront_fx_info *); -static int wffx_init (void); - -static int wavefront_delete_sample (int sampnum); -static int wavefront_find_free_sample (void); - -/* From wf_midi.c */ - -extern int virtual_midi_enable (void); -extern int virtual_midi_disable (void); -extern int detect_wf_mpu (int, int); -extern int install_wf_mpu (void); -extern int uninstall_wf_mpu (void); - -typedef struct { - int cmd; - char *action; - unsigned int read_cnt; - unsigned int write_cnt; - int need_ack; -} wavefront_command; - -static struct { - int errno; - const char *errstr; -} wavefront_errors[] = { - { 0x01, "Bad sample number" }, - { 0x02, "Out of sample memory" }, - { 0x03, "Bad patch number" }, - { 0x04, "Error in number of voices" }, - { 0x06, "Sample load already in progress" }, - { 0x0B, "No sample load request pending" }, - { 0x0E, "Bad MIDI channel number" }, - { 0x10, "Download Record Error" }, - { 0x80, "Success" }, - { 0 } -}; - -#define NEEDS_ACK 1 - -static wavefront_command wavefront_commands[] = { - { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, - { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, - { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, - { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, - { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, - { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, - { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, - { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, - { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, - { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, - { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, - { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, - { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, - { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, - { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, - { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, - { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, - { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, - { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, - { WFC_DOWNLOAD_SAMPLE, "download sample", - 0, WF_SAMPLE_BYTES, NEEDS_ACK }, - { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, - { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", - 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, - { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, - - /* This command requires a variable number of bytes to be written. - There is a hack in wavefront_cmd() to support this. The actual - count is passed in as the read buffer ptr, cast appropriately. - Ugh. - */ - - { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, - - /* This one is a hack as well. We just read the first byte of the - response, don't fetch an ACK, and leave the rest to the - calling function. Ugly, ugly, ugly. - */ - - { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, - { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", - 0, WF_ALIAS_BYTES, NEEDS_ACK }, - { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, - { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, - { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, - { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, - { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, - { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, - { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, - { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, - { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, - { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, - NEEDS_ACK}, - { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, - { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", - 0, 1, NEEDS_ACK }, - { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, - { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", - 32, 0, 0 }, - { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, - { 0x00 } -}; - -static const char * -wavefront_errorstr (int errnum) - -{ - int i; - - for (i = 0; wavefront_errors[i].errstr; i++) { - if (wavefront_errors[i].errno == errnum) { - return wavefront_errors[i].errstr; - } - } - - return "Unknown WaveFront error"; -} - -static wavefront_command * -wavefront_get_command (int cmd) - -{ - int i; - - for (i = 0; wavefront_commands[i].cmd != 0; i++) { - if (cmd == wavefront_commands[i].cmd) { - return &wavefront_commands[i]; - } - } - - return (wavefront_command *) 0; -} - -static inline int -wavefront_status (void) - -{ - return inb (dev.status_port); -} - -static int -wavefront_wait (int mask) - -{ - int i; - - for (i = 0; i < wait_polls; i++) - if (wavefront_status() & mask) - return 1; - - for (i = 0; i < sleep_tries; i++) { - - if (wavefront_status() & mask) { - set_current_state(TASK_RUNNING); - return 1; - } - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(sleep_length); - if (signal_pending(current)) - break; - } - - set_current_state(TASK_RUNNING); - return 0; -} - -static int -wavefront_read (void) - -{ - if (wavefront_wait (STAT_CAN_READ)) - return inb (dev.data_port); - - DPRINT (WF_DEBUG_DATA, "read timeout.\n"); - - return -1; -} - -static int -wavefront_write (unsigned char data) - -{ - if (wavefront_wait (STAT_CAN_WRITE)) { - outb (data, dev.data_port); - return 0; - } - - DPRINT (WF_DEBUG_DATA, "write timeout.\n"); - - return -1; -} - -static int -wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf) - -{ - int ack; - int i; - int c; - wavefront_command *wfcmd; - - if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { - printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n", - cmd); - return 1; - } - - /* Hack to handle the one variable-size write command. See - wavefront_send_multisample() for the other half of this - gross and ugly strategy. - */ - - if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { - wfcmd->write_cnt = (unsigned int) rbuf; - rbuf = NULL; - } - - DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", - cmd, wfcmd->action, wfcmd->read_cnt, - wfcmd->write_cnt, wfcmd->need_ack); - - if (wavefront_write (cmd)) { - DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " - "0x%x [%s].\n", - cmd, wfcmd->action); - return 1; - } - - if (wfcmd->write_cnt > 0) { - DPRINT (WF_DEBUG_DATA, "writing %d bytes " - "for 0x%x\n", - wfcmd->write_cnt, cmd); - - for (i = 0; i < wfcmd->write_cnt; i++) { - if (wavefront_write (wbuf[i])) { - DPRINT (WF_DEBUG_IO, "bad write for byte " - "%d of 0x%x [%s].\n", - i, cmd, wfcmd->action); - return 1; - } - - DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", - i, wbuf[i]); - } - } - - if (wfcmd->read_cnt > 0) { - DPRINT (WF_DEBUG_DATA, "reading %d ints " - "for 0x%x\n", - wfcmd->read_cnt, cmd); - - for (i = 0; i < wfcmd->read_cnt; i++) { - - if ((c = wavefront_read()) == -1) { - DPRINT (WF_DEBUG_IO, "bad read for byte " - "%d of 0x%x [%s].\n", - i, cmd, wfcmd->action); - return 1; - } - - /* Now handle errors. Lots of special cases here */ - - if (c == 0xff) { - if ((c = wavefront_read ()) == -1) { - DPRINT (WF_DEBUG_IO, "bad read for " - "error byte at " - "read byte %d " - "of 0x%x [%s].\n", - i, cmd, - wfcmd->action); - return 1; - } - - /* Can you believe this madness ? */ - - if (c == 1 && - wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { - rbuf[0] = WF_ST_EMPTY; - return (0); - - } else if (c == 3 && - wfcmd->cmd == WFC_UPLOAD_PATCH) { - - return 3; - - } else if (c == 1 && - wfcmd->cmd == WFC_UPLOAD_PROGRAM) { - - return 1; - - } else { - - DPRINT (WF_DEBUG_IO, "error %d (%s) " - "during " - "read for byte " - "%d of 0x%x " - "[%s].\n", - c, - wavefront_errorstr (c), - i, cmd, - wfcmd->action); - return 1; - - } - - } else { - rbuf[i] = c; - } - - DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); - } - } - - if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { - - DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); - - /* Some commands need an ACK, but return zero instead - of the standard value. - */ - - if ((ack = wavefront_read()) == 0) { - ack = WF_ACK; - } - - if (ack != WF_ACK) { - if (ack == -1) { - DPRINT (WF_DEBUG_IO, "cannot read ack for " - "0x%x [%s].\n", - cmd, wfcmd->action); - return 1; - - } else { - int err = -1; /* something unknown */ - - if (ack == 0xff) { /* explicit error */ - - if ((err = wavefront_read ()) == -1) { - DPRINT (WF_DEBUG_DATA, - "cannot read err " - "for 0x%x [%s].\n", - cmd, wfcmd->action); - } - } - - DPRINT (WF_DEBUG_IO, "0x%x [%s] " - "failed (0x%x, 0x%x, %s)\n", - cmd, wfcmd->action, ack, err, - wavefront_errorstr (err)); - - return -err; - } - } - - DPRINT (WF_DEBUG_DATA, "ack received " - "for 0x%x [%s]\n", - cmd, wfcmd->action); - } else { - - DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " - "ACK (%d,%d,%d)\n", - cmd, wfcmd->action, wfcmd->read_cnt, - wfcmd->write_cnt, wfcmd->need_ack); - } - - return 0; - -} - -/*********************************************************************** -WaveFront: data munging - -Things here are weird. All data written to the board cannot -have its most significant bit set. Any data item with values -potentially > 0x7F (127) must be split across multiple bytes. - -Sometimes, we need to munge numeric values that are represented on -the x86 side as 8-32 bit values. Sometimes, we need to munge data -that is represented on the x86 side as an array of bytes. The most -efficient approach to handling both cases seems to be to use 2 -different functions for munging and 2 for de-munging. This avoids -weird casting and worrying about bit-level offsets. - -**********************************************************************/ - -static -unsigned char * -munge_int32 (unsigned int src, - unsigned char *dst, - unsigned int dst_size) -{ - int i; - - for (i = 0;i < dst_size; i++) { - *dst = src & 0x7F; /* Mask high bit of LSB */ - src = src >> 7; /* Rotate Right 7 bits */ - /* Note: we leave the upper bits in place */ - - dst++; - }; - return dst; -}; - -static int -demunge_int32 (unsigned char* src, int src_size) - -{ - int i; - int outval = 0; - - for (i = src_size - 1; i >= 0; i--) { - outval=(outval<<7)+src[i]; - } - - return outval; -}; - -static -unsigned char * -munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) - -{ - int i; - unsigned int last = dst_size / 2; - - for (i = 0; i < last; i++) { - *dst++ = src[i] & 0x7f; - *dst++ = src[i] >> 7; - } - return dst; -} - -static -unsigned char * -demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) - -{ - int i; - unsigned char *end = src + src_bytes; - - end = src + src_bytes; - - /* NOTE: src and dst *CAN* point to the same address */ - - for (i = 0; src != end; i++) { - dst[i] = *src++; - dst[i] |= (*src++)<<7; - } - - return dst; -} - -/*********************************************************************** -WaveFront: sample, patch and program management. -***********************************************************************/ - -static int -wavefront_delete_sample (int sample_num) - -{ - unsigned char wbuf[2]; - int x; - - wbuf[0] = sample_num & 0x7f; - wbuf[1] = sample_num >> 7; - - if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, NULL, wbuf)) == 0) { - dev.sample_status[sample_num] = WF_ST_EMPTY; - } - - return x; -} - -static int -wavefront_get_sample_status (int assume_rom) - -{ - int i; - unsigned char rbuf[32], wbuf[32]; - unsigned int sc_real, sc_alias, sc_multi; - - /* check sample status */ - - if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME "cannot request sample count.\n"); - return -1; - } - - sc_real = sc_alias = sc_multi = dev.samples_used = 0; - - for (i = 0; i < WF_MAX_SAMPLE; i++) { - - wbuf[0] = i & 0x7f; - wbuf[1] = i >> 7; - - if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "cannot identify sample " - "type of slot %d\n", i); - dev.sample_status[i] = WF_ST_EMPTY; - continue; - } - - dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); - - if (assume_rom) { - dev.sample_status[i] |= WF_SLOT_ROM; - } - - switch (rbuf[0] & WF_ST_MASK) { - case WF_ST_SAMPLE: - sc_real++; - break; - case WF_ST_MULTISAMPLE: - sc_multi++; - break; - case WF_ST_ALIAS: - sc_alias++; - break; - case WF_ST_EMPTY: - break; - - default: - printk (KERN_WARNING LOGNAME "unknown sample type for " - "slot %d (0x%x)\n", - i, rbuf[0]); - } - - if (rbuf[0] != WF_ST_EMPTY) { - dev.samples_used++; - } - } - - printk (KERN_INFO LOGNAME - "%d samples used (%d real, %d aliases, %d multi), " - "%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi, - WF_MAX_SAMPLE - dev.samples_used); - - - return (0); - -} - -static int -wavefront_get_patch_status (void) - -{ - unsigned char patchbuf[WF_PATCH_BYTES]; - unsigned char patchnum[2]; - wavefront_patch *p; - int i, x, cnt, cnt2; - - for (i = 0; i < WF_MAX_PATCH; i++) { - patchnum[0] = i & 0x7f; - patchnum[1] = i >> 7; - - if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf, - patchnum)) == 0) { - - dev.patch_status[i] |= WF_SLOT_FILLED; - p = (wavefront_patch *) patchbuf; - dev.sample_status - [p->sample_number|(p->sample_msb<<7)] |= - WF_SLOT_USED; - - } else if (x == 3) { /* Bad patch number */ - dev.patch_status[i] = 0; - } else { - printk (KERN_ERR LOGNAME "upload patch " - "error 0x%x\n", x); - dev.patch_status[i] = 0; - return 1; - } - } - - /* program status has already filled in slot_used bits */ - - for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { - if (dev.patch_status[i] & WF_SLOT_FILLED) { - cnt++; - } - if (dev.patch_status[i] & WF_SLOT_USED) { - cnt2++; - } - - } - printk (KERN_INFO LOGNAME - "%d patch slots filled, %d in use\n", cnt, cnt2); - - return (0); -} - -static int -wavefront_get_program_status (void) - -{ - unsigned char progbuf[WF_PROGRAM_BYTES]; - wavefront_program prog; - unsigned char prognum; - int i, x, l, cnt; - - for (i = 0; i < WF_MAX_PROGRAM; i++) { - prognum = i; - - if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf, - &prognum)) == 0) { - - dev.prog_status[i] |= WF_SLOT_USED; - - demunge_buf (progbuf, (unsigned char *) &prog, - WF_PROGRAM_BYTES); - - for (l = 0; l < WF_NUM_LAYERS; l++) { - if (prog.layer[l].mute) { - dev.patch_status - [prog.layer[l].patch_number] |= - WF_SLOT_USED; - } - } - } else if (x == 1) { /* Bad program number */ - dev.prog_status[i] = 0; - } else { - printk (KERN_ERR LOGNAME "upload program " - "error 0x%x\n", x); - dev.prog_status[i] = 0; - } - } - - for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { - if (dev.prog_status[i]) { - cnt++; - } - } - - printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt); - - return (0); -} - -static int -wavefront_send_patch (wavefront_patch_info *header) - -{ - unsigned char buf[WF_PATCH_BYTES+2]; - unsigned char *bptr; - - DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", - header->number); - - dev.patch_status[header->number] |= WF_SLOT_FILLED; - - bptr = buf; - bptr = munge_int32 (header->number, buf, 2); - munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); - - if (wavefront_cmd (WFC_DOWNLOAD_PATCH, NULL, buf)) { - printk (KERN_ERR LOGNAME "download patch failed\n"); - return -(EIO); - } - - return (0); -} - -static int -wavefront_send_program (wavefront_patch_info *header) - -{ - unsigned char buf[WF_PROGRAM_BYTES+1]; - int i; - - DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", - header->number); - - dev.prog_status[header->number] = WF_SLOT_USED; - - /* XXX need to zero existing SLOT_USED bit for program_status[i] - where `i' is the program that's being (potentially) overwritten. - */ - - for (i = 0; i < WF_NUM_LAYERS; i++) { - if (header->hdr.pr.layer[i].mute) { - dev.patch_status[header->hdr.pr.layer[i].patch_number] |= - WF_SLOT_USED; - - /* XXX need to mark SLOT_USED for sample used by - patch_number, but this means we have to load it. Ick. - */ - } - } - - buf[0] = header->number; - munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); - - if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, NULL, buf)) { - printk (KERN_WARNING LOGNAME "download patch failed\n"); - return -(EIO); - } - - return (0); -} - -static int -wavefront_freemem (void) - -{ - char rbuf[8]; - - if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, NULL)) { - printk (KERN_WARNING LOGNAME "can't get memory stats.\n"); - return -1; - } else { - return demunge_int32 (rbuf, 4); - } -} - -static int -wavefront_send_sample (wavefront_patch_info *header, - UINT16 __user *dataptr, - int data_is_unsigned) - -{ - /* samples are downloaded via a 16-bit wide i/o port - (you could think of it as 2 adjacent 8-bit wide ports - but its less efficient that way). therefore, all - the blocksizes and so forth listed in the documentation, - and used conventionally to refer to sample sizes, - which are given in 8-bit units (bytes), need to be - divided by 2. - */ - - UINT16 sample_short; - UINT32 length; - UINT16 __user *data_end = NULL; - unsigned int i; - const int max_blksize = 4096/2; - unsigned int written; - unsigned int blocksize; - int dma_ack; - int blocknum; - unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; - unsigned char *shptr; - int skip = 0; - int initial_skip = 0; - - DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " - "type %d, %d bytes from %p\n", - header->size ? "" : "header ", - header->number, header->subkey, - header->size, - header->dataptr); - - if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { - int x; - - if ((x = wavefront_find_free_sample ()) < 0) { - return -ENOMEM; - } - printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x); - header->number = x; - } - - if (header->size) { - - /* XXX it's a debatable point whether or not RDONLY semantics - on the ROM samples should cover just the sample data or - the sample header. For now, it only covers the sample data, - so anyone is free at all times to rewrite sample headers. - - My reason for this is that we have the sample headers - available in the WFB file for General MIDI, and so these - can always be reset if needed. The sample data, however, - cannot be recovered without a complete reset and firmware - reload of the ICS2115, which is a very expensive operation. - - So, doing things this way allows us to honor the notion of - "RESETSAMPLES" reasonably cheaply. Note however, that this - is done purely at user level: there is no WFB parser in - this driver, and so a complete reset (back to General MIDI, - or theoretically some other configuration) is the - responsibility of the user level library. - - To try to do this in the kernel would be a little - crazy: we'd need 158K of kernel space just to hold - a copy of the patch/program/sample header data. - */ - - if (dev.rom_samples_rdonly) { - if (dev.sample_status[header->number] & WF_SLOT_ROM) { - printk (KERN_ERR LOGNAME "sample slot %d " - "write protected\n", - header->number); - return -EACCES; - } - } - - wavefront_delete_sample (header->number); - } - - if (header->size) { - dev.freemem = wavefront_freemem (); - - if (dev.freemem < header->size) { - printk (KERN_ERR LOGNAME - "insufficient memory to " - "load %d byte sample.\n", - header->size); - return -ENOMEM; - } - - } - - skip = WF_GET_CHANNEL(&header->hdr.s); - - if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { - printk (KERN_ERR LOGNAME "channel selection only " - "possible on 16-bit samples"); - return -(EINVAL); - } - - switch (skip) { - case 0: - initial_skip = 0; - skip = 1; - break; - case 1: - initial_skip = 0; - skip = 2; - break; - case 2: - initial_skip = 1; - skip = 2; - break; - case 3: - initial_skip = 2; - skip = 3; - break; - case 4: - initial_skip = 3; - skip = 4; - break; - case 5: - initial_skip = 4; - skip = 5; - break; - case 6: - initial_skip = 5; - skip = 6; - break; - } - - DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " - "initial skip = %d, skip = %d\n", - WF_GET_CHANNEL (&header->hdr.s), - initial_skip, skip); - - /* Be safe, and zero the "Unused" bits ... */ - - WF_SET_CHANNEL(&header->hdr.s, 0); - - /* adjust size for 16 bit samples by dividing by two. We always - send 16 bits per write, even for 8 bit samples, so the length - is always half the size of the sample data in bytes. - */ - - length = header->size / 2; - - /* the data we're sent has not been munged, and in fact, the - header we have to send isn't just a munged copy either. - so, build the sample header right here. - */ - - shptr = &sample_hdr[0]; - - shptr = munge_int32 (header->number, shptr, 2); - - if (header->size) { - shptr = munge_int32 (length, shptr, 4); - } - - /* Yes, a 4 byte result doesn't contain all of the offset bits, - but the offset only uses 24 bits. - */ - - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), - shptr, 4); - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), - shptr, 4); - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), - shptr, 4); - shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), - shptr, 4); - - /* This one is truly weird. What kind of weirdo decided that in - a system dominated by 16 and 32 bit integers, they would use - a just 12 bits ? - */ - - shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); - - /* Why is this nybblified, when the MSB is *always* zero ? - Anyway, we can't take address of bitfield, so make a - good-faith guess at where it starts. - */ - - shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), - shptr, 2); - - if (wavefront_cmd (header->size ? - WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, - NULL, sample_hdr)) { - printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n", - header->size ? "" : "header "); - return -(EIO); - } - - if (header->size == 0) { - goto sent; /* Sorry. Just had to have one somewhere */ - } - - data_end = dataptr + length; - - /* Do any initial skip over an unused channel's data */ - - dataptr += initial_skip; - - for (written = 0, blocknum = 0; - written < length; written += max_blksize, blocknum++) { - - if ((length - written) > max_blksize) { - blocksize = max_blksize; - } else { - /* round to nearest 16-byte value */ - blocksize = ((length-written+7)&~0x7); - } - - if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, NULL, NULL)) { - printk (KERN_WARNING LOGNAME "download block " - "request refused.\n"); - return -(EIO); - } - - for (i = 0; i < blocksize; i++) { - - if (dataptr < data_end) { - - __get_user (sample_short, dataptr); - dataptr += skip; - - if (data_is_unsigned) { /* GUS ? */ - - if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { - - /* 8 bit sample - resolution, sign - extend both bytes. - */ - - ((unsigned char*) - &sample_short)[0] += 0x7f; - ((unsigned char*) - &sample_short)[1] += 0x7f; - - } else { - - /* 16 bit sample - resolution, sign - extend the MSB. - */ - - sample_short += 0x7fff; - } - } - - } else { - - /* In padding section of final block: - - Don't fetch unsupplied data from - user space, just continue with - whatever the final value was. - */ - } - - if (i < blocksize - 1) { - outw (sample_short, dev.block_port); - } else { - outw (sample_short, dev.last_block_port); - } - } - - /* Get "DMA page acknowledge", even though its really - nothing to do with DMA at all. - */ - - if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) { - if (dma_ack == -1) { - printk (KERN_ERR LOGNAME "upload sample " - "DMA ack timeout\n"); - return -(EIO); - } else { - printk (KERN_ERR LOGNAME "upload sample " - "DMA ack error 0x%x\n", - dma_ack); - return -(EIO); - } - } - } - - dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); - - /* Note, label is here because sending the sample header shouldn't - alter the sample_status info at all. - */ - - sent: - return (0); -} - -static int -wavefront_send_alias (wavefront_patch_info *header) - -{ - unsigned char alias_hdr[WF_ALIAS_BYTES]; - - DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " - "alias for %d\n", - header->number, - header->hdr.a.OriginalSample); - - munge_int32 (header->number, &alias_hdr[0], 2); - munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); - munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), - &alias_hdr[4], 4); - munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), - &alias_hdr[8], 4); - munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), - &alias_hdr[12], 4); - munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), - &alias_hdr[16], 4); - munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); - munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); - - if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) { - printk (KERN_ERR LOGNAME "download alias failed.\n"); - return -(EIO); - } - - dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); - - return (0); -} - -static int -wavefront_send_multisample (wavefront_patch_info *header) -{ - int i; - int num_samples; - unsigned char msample_hdr[WF_MSAMPLE_BYTES]; - - munge_int32 (header->number, &msample_hdr[0], 2); - - /* You'll recall at this point that the "number of samples" value - in a wavefront_multisample struct is actually the log2 of the - real number of samples. - */ - - num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); - msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; - - DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", - header->number, - header->hdr.ms.NumberOfSamples, - num_samples); - - for (i = 0; i < num_samples; i++) { - DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", - i, header->hdr.ms.SampleNumber[i]); - munge_int32 (header->hdr.ms.SampleNumber[i], - &msample_hdr[3+(i*2)], 2); - } - - /* Need a hack here to pass in the number of bytes - to be written to the synth. This is ugly, and perhaps - one day, I'll fix it. - */ - - if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, - (unsigned char *) ((num_samples*2)+3), - msample_hdr)) { - printk (KERN_ERR LOGNAME "download of multisample failed.\n"); - return -(EIO); - } - - dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); - - return (0); -} - -static int -wavefront_fetch_multisample (wavefront_patch_info *header) -{ - int i; - unsigned char log_ns[1]; - unsigned char number[2]; - int num_samples; - - munge_int32 (header->number, number, 2); - - if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { - printk (KERN_ERR LOGNAME "upload multisample failed.\n"); - return -(EIO); - } - - DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", - header->number, log_ns[0]); - - header->hdr.ms.NumberOfSamples = log_ns[0]; - - /* get the number of samples ... */ - - num_samples = (1 << log_ns[0]); - - for (i = 0; i < num_samples; i++) { - s8 d[2]; - - if ((d[0] = wavefront_read ()) == -1) { - printk (KERN_ERR LOGNAME "upload multisample failed " - "during sample loop.\n"); - return -(EIO); - } - - if ((d[1] = wavefront_read ()) == -1) { - printk (KERN_ERR LOGNAME "upload multisample failed " - "during sample loop.\n"); - return -(EIO); - } - - header->hdr.ms.SampleNumber[i] = - demunge_int32 ((unsigned char *) d, 2); - - DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", - i, header->hdr.ms.SampleNumber[i]); - } - - return (0); -} - - -static int -wavefront_send_drum (wavefront_patch_info *header) - -{ - unsigned char drumbuf[WF_DRUM_BYTES]; - wavefront_drum *drum = &header->hdr.d; - int i; - - DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " - "note %d, patch = %d\n", - header->number, drum->PatchNumber); - - drumbuf[0] = header->number & 0x7f; - - for (i = 0; i < 4; i++) { - munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); - } - - if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) { - printk (KERN_ERR LOGNAME "download drum failed.\n"); - return -(EIO); - } - - return (0); -} - -static int -wavefront_find_free_sample (void) - -{ - int i; - - for (i = 0; i < WF_MAX_SAMPLE; i++) { - if (!(dev.sample_status[i] & WF_SLOT_FILLED)) { - return i; - } - } - printk (KERN_WARNING LOGNAME "no free sample slots!\n"); - return -1; -} - -static int -wavefront_find_free_patch (void) - -{ - int i; - - for (i = 0; i < WF_MAX_PATCH; i++) { - if (!(dev.patch_status[i] & WF_SLOT_FILLED)) { - return i; - } - } - printk (KERN_WARNING LOGNAME "no free patch slots!\n"); - return -1; -} - -static int -log2_2048(int n) - -{ - int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, - 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, - 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, - 9510, 9626, 9738, 9845, 9949, 10049, 10146}; - int i; - - /* Returns 2048*log2(n) */ - - /* FIXME: this is like doing integer math - on quantum particles (RuN) */ - - i=0; - while(n>=32*256) { - n>>=8; - i+=2048*8; - } - while(n>=32) { - n>>=1; - i+=2048; - } - i+=tbl[n]; - return(i); -} - -static int -wavefront_load_gus_patch (int devno, int format, const char __user *addr, - int offs, int count, int pmgr_flag) -{ - struct patch_info guspatch; - wavefront_patch_info *samp, *pat, *prog; - wavefront_patch *patp; - wavefront_sample *sampp; - wavefront_program *progp; - - int i,base_note; - long sizeof_patch; - int rc = -ENOMEM; - - samp = kmalloc(3 * sizeof(wavefront_patch_info), GFP_KERNEL); - if (!samp) - goto free_fail; - pat = samp + 1; - prog = pat + 1; - - /* Copy in the header of the GUS patch */ - - sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; - if (copy_from_user(&((char *) &guspatch)[offs], - &(addr)[offs], sizeof_patch - offs)) { - rc = -EFAULT; - goto free_fail; - } - - if ((i = wavefront_find_free_patch ()) == -1) { - rc = -EBUSY; - goto free_fail; - } - pat->number = i; - pat->subkey = WF_ST_PATCH; - patp = &pat->hdr.p; - - if ((i = wavefront_find_free_sample ()) == -1) { - rc = -EBUSY; - goto free_fail; - } - samp->number = i; - samp->subkey = WF_ST_SAMPLE; - samp->size = guspatch.len; - sampp = &samp->hdr.s; - - prog->number = guspatch.instr_no; - progp = &prog->hdr.pr; - - /* Setup the patch structure */ - - patp->amplitude_bias=guspatch.volume; - patp->portamento=0; - patp->sample_number= samp->number & 0xff; - patp->sample_msb= samp->number >> 8; - patp->pitch_bend= /*12*/ 0; - patp->mono=1; - patp->retrigger=1; - patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; - patp->frequency_bias=0; - patp->restart=0; - patp->reuse=0; - patp->reset_lfo=1; - patp->fm_src2=0; - patp->fm_src1=WF_MOD_MOD_WHEEL; - patp->am_src=WF_MOD_PRESSURE; - patp->am_amount=127; - patp->fc1_mod_amount=0; - patp->fc2_mod_amount=0; - patp->fm_amount1=0; - patp->fm_amount2=0; - patp->envelope1.attack_level=127; - patp->envelope1.decay1_level=127; - patp->envelope1.decay2_level=127; - patp->envelope1.sustain_level=127; - patp->envelope1.release_level=0; - patp->envelope2.attack_velocity=127; - patp->envelope2.attack_level=127; - patp->envelope2.decay1_level=127; - patp->envelope2.decay2_level=127; - patp->envelope2.sustain_level=127; - patp->envelope2.release_level=0; - patp->envelope2.attack_velocity=127; - patp->randomizer=0; - - /* Program for this patch */ - - progp->layer[0].patch_number= pat->number; /* XXX is this right ? */ - progp->layer[0].mute=1; - progp->layer[0].pan_or_mod=1; - progp->layer[0].pan=7; - progp->layer[0].mix_level=127 /* guspatch.volume */; - progp->layer[0].split_type=0; - progp->layer[0].split_point=0; - progp->layer[0].play_below=0; - - for (i = 1; i < 4; i++) { - progp->layer[i].mute=0; - } - - /* Sample data */ - - sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); - - for (base_note=0; - note_to_freq (base_note) < guspatch.base_note; - base_note++); - - if ((guspatch.base_note-note_to_freq(base_note)) - >(note_to_freq(base_note)-guspatch.base_note)) - base_note++; - - printk(KERN_DEBUG "ref freq=%d,base note=%d\n", - guspatch.base_freq, - base_note); - - sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) - + base_note*171); - printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); - sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; - sampp->sampleStartOffset.Fraction=0; - sampp->sampleStartOffset.Integer=0; - sampp->loopStartOffset.Fraction=0; - sampp->loopStartOffset.Integer=guspatch.loop_start - >>((guspatch.mode&WAVE_16_BITS) ? 1:0); - sampp->loopEndOffset.Fraction=0; - sampp->loopEndOffset.Integer=guspatch.loop_end - >>((guspatch.mode&WAVE_16_BITS) ? 1:0); - sampp->sampleEndOffset.Fraction=0; - sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); - sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; - sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; - - /* Now ship it down */ - - wavefront_send_sample (samp, - (unsigned short __user *) &(addr)[sizeof_patch], - (guspatch.mode & WAVE_UNSIGNED) ? 1:0); - wavefront_send_patch (pat); - wavefront_send_program (prog); - - /* Now pan as best we can ... use the slave/internal MIDI device - number if it exists (since it talks to the WaveFront), or the - master otherwise. - */ - - if (dev.mididev > 0) { - midi_synth_controller (dev.mididev, guspatch.instr_no, 10, - ((guspatch.panning << 4) > 127) ? - 127 : (guspatch.panning << 4)); - } - rc = 0; - -free_fail: - kfree(samp); - return rc; -} - -static int -wavefront_load_patch (const char __user *addr) - - -{ - wavefront_patch_info header; - - if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - - sizeof(wavefront_any))) { - printk (KERN_WARNING LOGNAME "bad address for load patch.\n"); - return -EFAULT; - } - - DPRINT (WF_DEBUG_LOAD_PATCH, "download " - "Sample type: %d " - "Sample number: %d " - "Sample size: %d\n", - header.subkey, - header.number, - header.size); - - switch (header.subkey) { - case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ - - if (copy_from_user((unsigned char *) &header.hdr.s, - (unsigned char __user *) header.hdrptr, - sizeof (wavefront_sample))) - return -EFAULT; - - return wavefront_send_sample (&header, header.dataptr, 0); - - case WF_ST_MULTISAMPLE: - - if (copy_from_user(&header.hdr.s, header.hdrptr, - sizeof(wavefront_multisample))) - return -EFAULT; - - return wavefront_send_multisample (&header); - - - case WF_ST_ALIAS: - - if (copy_from_user(&header.hdr.a, header.hdrptr, - sizeof (wavefront_alias))) - return -EFAULT; - - return wavefront_send_alias (&header); - - case WF_ST_DRUM: - if (copy_from_user(&header.hdr.d, header.hdrptr, - sizeof (wavefront_drum))) - return -EFAULT; - - return wavefront_send_drum (&header); - - case WF_ST_PATCH: - if (copy_from_user(&header.hdr.p, header.hdrptr, - sizeof (wavefront_patch))) - return -EFAULT; - - return wavefront_send_patch (&header); - - case WF_ST_PROGRAM: - if (copy_from_user(&header.hdr.pr, header.hdrptr, - sizeof (wavefront_program))) - return -EFAULT; - - return wavefront_send_program (&header); - - default: - printk (KERN_ERR LOGNAME "unknown patch type %d.\n", - header.subkey); - return -(EINVAL); - } - - return 0; -} - -/*********************************************************************** -WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces -***********************************************************************/ - -static void -process_sample_hdr (UCHAR8 *buf) - -{ - wavefront_sample s; - UCHAR8 *ptr; - - ptr = buf; - - /* The board doesn't send us an exact copy of a "wavefront_sample" - in response to an Upload Sample Header command. Instead, we - have to convert the data format back into our data structure, - just as in the Download Sample command, where we have to do - something very similar in the reverse direction. - */ - - *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; - *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; - - s.SampleResolution = *ptr & 0x3; - s.Loop = *ptr & 0x8; - s.Bidirectional = *ptr & 0x10; - s.Reverse = *ptr & 0x40; - - /* Now copy it back to where it came from */ - - memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); -} - -static int -wavefront_synth_control (int cmd, wavefront_control *wc) - -{ - unsigned char patchnumbuf[2]; - int i; - - DPRINT (WF_DEBUG_CMD, "synth control with " - "cmd 0x%x\n", wc->cmd); - - /* Pre-handling of or for various commands */ - - switch (wc->cmd) { - case WFC_DISABLE_INTERRUPTS: - printk (KERN_INFO LOGNAME "interrupts disabled.\n"); - outb (0x80|0x20, dev.control_port); - dev.interrupts_on = 0; - return 0; - - case WFC_ENABLE_INTERRUPTS: - printk (KERN_INFO LOGNAME "interrupts enabled.\n"); - outb (0x80|0x40|0x20, dev.control_port); - dev.interrupts_on = 1; - return 0; - - case WFC_INTERRUPT_STATUS: - wc->rbuf[0] = dev.interrupts_on; - return 0; - - case WFC_ROMSAMPLES_RDONLY: - dev.rom_samples_rdonly = wc->wbuf[0]; - wc->status = 0; - return 0; - - case WFC_IDENTIFY_SLOT_TYPE: - i = wc->wbuf[0] | (wc->wbuf[1] << 7); - if (i <0 || i >= WF_MAX_SAMPLE) { - printk (KERN_WARNING LOGNAME "invalid slot ID %d\n", - i); - wc->status = EINVAL; - return 0; - } - wc->rbuf[0] = dev.sample_status[i]; - wc->status = 0; - return 0; - - case WFC_DEBUG_DRIVER: - dev.debug = wc->wbuf[0]; - printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug); - return 0; - - case WFC_FX_IOCTL: - wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]); - return 0; - - case WFC_UPLOAD_PATCH: - munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2); - memcpy (wc->wbuf, patchnumbuf, 2); - break; - - case WFC_UPLOAD_MULTISAMPLE: - /* multisamples have to be handled differently, and - cannot be dealt with properly by wavefront_cmd() alone. - */ - wc->status = wavefront_fetch_multisample - ((wavefront_patch_info *) wc->rbuf); - return 0; - - case WFC_UPLOAD_SAMPLE_ALIAS: - printk (KERN_INFO LOGNAME "support for sample alias upload " - "being considered.\n"); - wc->status = EINVAL; - return -EINVAL; - } - - wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf); - - /* Post-handling of certain commands. - - In particular, if the command was an upload, demunge the data - so that the user-level doesn't have to think about it. - */ - - if (wc->status == 0) { - switch (wc->cmd) { - /* intercept any freemem requests so that we know - we are always current with the user-level view - of things. - */ - - case WFC_REPORT_FREE_MEMORY: - dev.freemem = demunge_int32 (wc->rbuf, 4); - break; - - case WFC_UPLOAD_PATCH: - demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); - break; - - case WFC_UPLOAD_PROGRAM: - demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); - break; - - case WFC_UPLOAD_EDRUM_PROGRAM: - demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); - break; - - case WFC_UPLOAD_SAMPLE_HEADER: - process_sample_hdr (wc->rbuf); - break; - - case WFC_UPLOAD_SAMPLE_ALIAS: - printk (KERN_INFO LOGNAME "support for " - "sample aliases still " - "being considered.\n"); - break; - - case WFC_VMIDI_OFF: - if (virtual_midi_disable () < 0) { - return -(EIO); - } - break; - - case WFC_VMIDI_ON: - if (virtual_midi_enable () < 0) { - return -(EIO); - } - break; - } - } - - return 0; -} - - -/***********************************************************************/ -/* WaveFront: Linux file system interface (for access via raw synth) */ -/***********************************************************************/ - -static int -wavefront_open (struct inode *inode, struct file *file) -{ - /* XXX fix me */ - dev.opened = file->f_flags; - return 0; -} - -static int -wavefront_release(struct inode *inode, struct file *file) -{ - lock_kernel(); - dev.opened = 0; - dev.debug = 0; - unlock_kernel(); - return 0; -} - -static int -wavefront_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - wavefront_control wc; - int err; - - switch (cmd) { - - case WFCTL_WFCMD: - if (copy_from_user(&wc, (void __user *) arg, sizeof (wc))) - return -EFAULT; - - if ((err = wavefront_synth_control (cmd, &wc)) == 0) { - if (copy_to_user ((void __user *) arg, &wc, sizeof (wc))) - return -EFAULT; - } - - return err; - - case WFCTL_LOAD_SPP: - return wavefront_load_patch ((const char __user *) arg); - - default: - printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd); - return -(EINVAL); - - } - return 0; -} - -static /*const*/ struct file_operations wavefront_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = wavefront_ioctl, - .open = wavefront_open, - .release = wavefront_release, -}; - - -/***********************************************************************/ -/* WaveFront: OSS installation and support interface */ -/***********************************************************************/ - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - -static struct synth_info wavefront_info = -{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, - 0, 32, 0, 0, SYNTH_CAP_INPUT}; - -static int -wavefront_oss_open (int devno, int mode) - -{ - dev.opened = mode; - return 0; -} - -static void -wavefront_oss_close (int devno) - -{ - dev.opened = 0; - dev.debug = 0; - return; -} - -static int -wavefront_oss_ioctl (int devno, unsigned int cmd, void __user * arg) - -{ - wavefront_control wc; - int err; - - switch (cmd) { - case SNDCTL_SYNTH_INFO: - if(copy_to_user(arg, &wavefront_info, sizeof (wavefront_info))) - return -EFAULT; - return 0; - - case SNDCTL_SEQ_RESETSAMPLES: -// printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n"); - return 0; /* don't force an error */ - - case SNDCTL_SEQ_PERCMODE: - return 0; /* don't force an error */ - - case SNDCTL_SYNTH_MEMAVL: - if ((dev.freemem = wavefront_freemem ()) < 0) { - printk (KERN_ERR LOGNAME "cannot get memory size\n"); - return -EIO; - } else { - return dev.freemem; - } - break; - - case SNDCTL_SYNTH_CONTROL: - if(copy_from_user (&wc, arg, sizeof (wc))) - err = -EFAULT; - else if ((err = wavefront_synth_control (cmd, &wc)) == 0) { - if(copy_to_user (arg, &wc, sizeof (wc))) - err = -EFAULT; - } - - return err; - - default: - return -(EINVAL); - } -} - -static int -wavefront_oss_load_patch (int devno, int format, const char __user *addr, - int offs, int count, int pmgr_flag) -{ - - if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ - if (midi_load_patch == NULL) { - printk (KERN_ERR LOGNAME - "SYSEX not loadable: " - "no midi patch loader!\n"); - return -(EINVAL); - } - - return midi_load_patch (devno, format, addr, - offs, count, pmgr_flag); - - } else if (format == GUS_PATCH) { - return wavefront_load_gus_patch (devno, format, - addr, offs, count, pmgr_flag); - - } else if (format != WAVEFRONT_PATCH) { - printk (KERN_ERR LOGNAME "unknown patch format %d\n", format); - return -(EINVAL); - } - - if (count < sizeof (wavefront_patch_info)) { - printk (KERN_ERR LOGNAME "sample header too short\n"); - return -(EINVAL); - } - - /* "addr" points to a user-space wavefront_patch_info */ - - return wavefront_load_patch (addr); -} - -static struct synth_operations wavefront_operations = -{ - .owner = THIS_MODULE, - .id = "WaveFront", - .info = &wavefront_info, - .midi_dev = 0, - .synth_type = SYNTH_TYPE_SAMPLE, - .synth_subtype = SAMPLE_TYPE_WAVEFRONT, - .open = wavefront_oss_open, - .close = wavefront_oss_close, - .ioctl = wavefront_oss_ioctl, - .kill_note = midi_synth_kill_note, - .start_note = midi_synth_start_note, - .set_instr = midi_synth_set_instr, - .reset = midi_synth_reset, - .load_patch = midi_synth_load_patch, - .aftertouch = midi_synth_aftertouch, - .controller = midi_synth_controller, - .panning = midi_synth_panning, - .bender = midi_synth_bender, - .setup_voice = midi_synth_setup_voice -}; -#endif /* OSS_SUPPORT_SEQ */ - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL - -static void __init attach_wavefront (struct address_info *hw_config) -{ - (void) install_wavefront (); -} - -static int __init probe_wavefront (struct address_info *hw_config) -{ - return !detect_wavefront (hw_config->irq, hw_config->io_base); -} - -static void __exit unload_wavefront (struct address_info *hw_config) -{ - (void) uninstall_wavefront (); -} - -#endif /* OSS_SUPPORT_STATIC_INSTALL */ - -/***********************************************************************/ -/* WaveFront: Linux modular sound kernel installation interface */ -/***********************************************************************/ - -static irqreturn_t -wavefrontintr(int irq, void *dev_id, struct pt_regs *dummy) -{ - struct wf_config *hw = dev_id; - - /* - Some comments on interrupts. I attempted a version of this - driver that used interrupts throughout the code instead of - doing busy and/or sleep-waiting. Alas, it appears that once - the Motorola firmware is downloaded, the card *never* - generates an RX interrupt. These are successfully generated - during firmware loading, and after that wavefront_status() - reports that an interrupt is pending on the card from time - to time, but it never seems to be delivered to this - driver. Note also that wavefront_status() continues to - report that RX interrupts are enabled, suggesting that I - didn't goof up and disable them by mistake. - - Thus, I stepped back to a prior version of - wavefront_wait(), the only place where this really - matters. Its sad, but I've looked through the code to check - on things, and I really feel certain that the Motorola - firmware prevents RX-ready interrupts. - */ - - if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { - return IRQ_NONE; - } - - hw->irq_ok = 1; - hw->irq_cnt++; - wake_up_interruptible (&hw->interrupt_sleeper); - return IRQ_HANDLED; -} - -/* STATUS REGISTER - -0 Host Rx Interrupt Enable (1=Enabled) -1 Host Rx Register Full (1=Full) -2 Host Rx Interrupt Pending (1=Interrupt) -3 Unused -4 Host Tx Interrupt (1=Enabled) -5 Host Tx Register empty (1=Empty) -6 Host Tx Interrupt Pending (1=Interrupt) -7 Unused -*/ - -static int -wavefront_interrupt_bits (int irq) - -{ - int bits; - - switch (irq) { - case 9: - bits = 0x00; - break; - case 5: - bits = 0x08; - break; - case 12: - bits = 0x10; - break; - case 15: - bits = 0x18; - break; - - default: - printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq); - bits = -1; - } - - return bits; -} - -static void -wavefront_should_cause_interrupt (int val, int port, int timeout) - -{ - unsigned long flags; - - /* this will not help on SMP - but at least it compiles */ - spin_lock_irqsave(&lock, flags); - dev.irq_ok = 0; - outb (val,port); - interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout); - spin_unlock_irqrestore(&lock,flags); -} - -static int __init wavefront_hw_reset (void) -{ - int bits; - int hwv[2]; - unsigned long irq_mask; - short reported_irq; - - /* IRQ already checked in init_module() */ - - bits = wavefront_interrupt_bits (dev.irq); - - printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n"); - - irq_mask = probe_irq_on (); - - outb (0x0, dev.control_port); - outb (0x80 | 0x40 | bits, dev.data_port); - wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, - dev.control_port, - (reset_time*HZ)/100); - - reported_irq = probe_irq_off (irq_mask); - - if (reported_irq != dev.irq) { - if (reported_irq == 0) { - printk (KERN_ERR LOGNAME - "No unassigned interrupts detected " - "after h/w reset\n"); - } else if (reported_irq < 0) { - printk (KERN_ERR LOGNAME - "Multiple unassigned interrupts detected " - "after h/w reset\n"); - } else { - printk (KERN_ERR LOGNAME "autodetected IRQ %d not the " - "value provided (%d)\n", reported_irq, - dev.irq); - } - dev.irq = -1; - return 1; - } else { - printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n", - reported_irq); - } - - if (request_irq (dev.irq, wavefrontintr, - IRQF_DISABLED|IRQF_SHARED, - "wavefront synth", &dev) < 0) { - printk (KERN_WARNING LOGNAME "IRQ %d not available!\n", - dev.irq); - return 1; - } - - /* try reset of port */ - - outb (0x0, dev.control_port); - - /* At this point, the board is in reset, and the H/W initialization - register is accessed at the same address as the data port. - - Bit 7 - Enable IRQ Driver - 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs - 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. - - Bit 6 - MIDI Interface Select - - 0 - Use the MIDI Input from the 26-pin WaveBlaster - compatible header as the serial MIDI source - 1 - Use the MIDI Input from the 9-pin D connector as the - serial MIDI source. - - Bits 5:3 - IRQ Selection - 0 0 0 - IRQ 2/9 - 0 0 1 - IRQ 5 - 0 1 0 - IRQ 12 - 0 1 1 - IRQ 15 - 1 0 0 - Reserved - 1 0 1 - Reserved - 1 1 0 - Reserved - 1 1 1 - Reserved - - Bits 2:1 - Reserved - Bit 0 - Disable Boot ROM - 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM - 1 - memory accesses to 03FC30-03FFFFH are directed to external - storage. - - */ - - /* configure hardware: IRQ, enable interrupts, - plus external 9-pin MIDI interface selected - */ - - outb (0x80 | 0x40 | bits, dev.data_port); - - /* CONTROL REGISTER - - 0 Host Rx Interrupt Enable (1=Enabled) 0x1 - 1 Unused 0x2 - 2 Unused 0x4 - 3 Unused 0x8 - 4 Host Tx Interrupt Enable 0x10 - 5 Mute (0=Mute; 1=Play) 0x20 - 6 Master Interrupt Enable (1=Enabled) 0x40 - 7 Master Reset (0=Reset; 1=Run) 0x80 - - Take us out of reset, mute output, master + TX + RX interrupts on. - - We'll get an interrupt presumably to tell us that the TX - register is clear. - */ - - wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, - dev.control_port, - (reset_time*HZ)/100); - - /* Note: data port is now the data port, not the h/w initialization - port. - */ - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "intr not received after h/w un-reset.\n"); - goto gone_bad; - } - - dev.interrupts_on = 1; - - /* Note: data port is now the data port, not the h/w initialization - port. - - At this point, only "HW VERSION" or "DOWNLOAD OS" commands - will work. So, issue one of them, and wait for TX - interrupt. This can take a *long* time after a cold boot, - while the ISC ROM does its RAM test. The SDK says up to 4 - seconds - with 12MB of RAM on a Tropez+, it takes a lot - longer than that (~16secs). Note that the card understands - the difference between a warm and a cold boot, so - subsequent ISC2115 reboots (say, caused by module - reloading) will get through this much faster. - - XXX Interesting question: why is no RX interrupt received first ? - */ - - wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, - dev.data_port, ramcheck_time*HZ); - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "post-RAM-check interrupt not received.\n"); - goto gone_bad; - } - - if (!wavefront_wait (STAT_CAN_READ)) { - printk (KERN_WARNING LOGNAME - "no response to HW version cmd.\n"); - goto gone_bad; - } - - if ((hwv[0] = wavefront_read ()) == -1) { - printk (KERN_WARNING LOGNAME - "board not responding correctly.\n"); - goto gone_bad; - } - - if (hwv[0] == 0xFF) { /* NAK */ - - /* Board's RAM test failed. Try to read error code, - and tell us about it either way. - */ - - if ((hwv[0] = wavefront_read ()) == -1) { - printk (KERN_WARNING LOGNAME "on-board RAM test failed " - "(bad error code).\n"); - } else { - printk (KERN_WARNING LOGNAME "on-board RAM test failed " - "(error code: 0x%x).\n", - hwv[0]); - } - goto gone_bad; - } - - /* We're OK, just get the next byte of the HW version response */ - - if ((hwv[1] = wavefront_read ()) == -1) { - printk (KERN_WARNING LOGNAME "incorrect h/w response.\n"); - goto gone_bad; - } - - printk (KERN_INFO LOGNAME "hardware version %d.%d\n", - hwv[0], hwv[1]); - - return 0; - - - gone_bad: - if (dev.irq >= 0) { - free_irq (dev.irq, &dev); - dev.irq = -1; - } - return (1); -} - -static int __init detect_wavefront (int irq, int io_base) -{ - unsigned char rbuf[4], wbuf[4]; - - /* TB docs say the device takes up 8 ports, but we know that - if there is an FX device present (i.e. a Tropez+) it really - consumes 16. - */ - - if (!request_region (io_base, 16, "wavfront")) { - printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x " - "already in use - ignored\n", dev.base, - dev.base+15); - return -1; - } - - dev.irq = irq; - dev.base = io_base; - dev.israw = 0; - dev.debug = debug_default; - dev.interrupts_on = 0; - dev.irq_cnt = 0; - dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ - - if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { - - dev.fw_version[0] = rbuf[0]; - dev.fw_version[1] = rbuf[1]; - printk (KERN_INFO LOGNAME - "firmware %d.%d already loaded.\n", - rbuf[0], rbuf[1]); - - /* check that a command actually works */ - - if (wavefront_cmd (WFC_HARDWARE_VERSION, - rbuf, wbuf) == 0) { - dev.hw_version[0] = rbuf[0]; - dev.hw_version[1] = rbuf[1]; - } else { - printk (KERN_WARNING LOGNAME "not raw, but no " - "hardware version!\n"); - release_region (io_base, 16); - return 0; - } - - if (!wf_raw) { - /* will re-acquire region in install_wavefront() */ - release_region (io_base, 16); - return 1; - } else { - printk (KERN_INFO LOGNAME - "reloading firmware anyway.\n"); - dev.israw = 1; - } - - } else { - - dev.israw = 1; - printk (KERN_INFO LOGNAME - "no response to firmware probe, assume raw.\n"); - - } - - init_waitqueue_head (&dev.interrupt_sleeper); - - if (wavefront_hw_reset ()) { - printk (KERN_WARNING LOGNAME "hardware reset failed\n"); - release_region (io_base, 16); - return 0; - } - - /* Check for FX device, present only on Tropez+ */ - - dev.has_fx = (detect_wffx () == 0); - - /* will re-acquire region in install_wavefront() */ - release_region (io_base, 16); - return 1; -} - -#include "os.h" -#include -#include -#include -#include - - -static int -wavefront_download_firmware (char *path) - -{ - unsigned char section[WF_SECTION_MAX]; - char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ - int section_cnt_downloaded = 0; - int fd; - int c; - int i; - mm_segment_t fs; - - /* This tries to be a bit cleverer than the stuff Alan Cox did for - the generic sound firmware, in that it actually knows - something about the structure of the Motorola firmware. In - particular, it uses a version that has been stripped of the - 20K of useless header information, and had section lengths - added, making it possible to load the entire OS without any - [kv]malloc() activity, since the longest entity we ever read is - 42 bytes (well, WF_SECTION_MAX) long. - */ - - fs = get_fs(); - set_fs (get_ds()); - - if ((fd = sys_open (path, 0, 0)) < 0) { - printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n", - path); - return 1; - } - - while (1) { - int x; - - if ((x = sys_read (fd, §ion_length, sizeof (section_length))) != - sizeof (section_length)) { - printk (KERN_ERR LOGNAME "firmware read error.\n"); - goto failure; - } - - if (section_length == 0) { - break; - } - - if (sys_read (fd, section, section_length) != section_length) { - printk (KERN_ERR LOGNAME "firmware section " - "read error.\n"); - goto failure; - } - - /* Send command */ - - if (wavefront_write (WFC_DOWNLOAD_OS)) { - goto failure; - } - - for (i = 0; i < section_length; i++) { - if (wavefront_write (section[i])) { - goto failure; - } - } - - /* get ACK */ - - if (wavefront_wait (STAT_CAN_READ)) { - - if ((c = inb (dev.data_port)) != WF_ACK) { - - printk (KERN_ERR LOGNAME "download " - "of section #%d not " - "acknowledged, ack = 0x%x\n", - section_cnt_downloaded + 1, c); - goto failure; - - } - - } else { - printk (KERN_ERR LOGNAME "time out for firmware ACK.\n"); - goto failure; - } - - } - - sys_close (fd); - set_fs (fs); - return 0; - - failure: - sys_close (fd); - set_fs (fs); - printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); - return 1; -} - -static int __init wavefront_config_midi (void) -{ - unsigned char rbuf[4], wbuf[4]; - - if (detect_wf_mpu (dev.irq, dev.base) < 0) { - printk (KERN_WARNING LOGNAME - "could not find working MIDI device\n"); - return -1; - } - - if ((dev.mididev = install_wf_mpu ()) < 0) { - printk (KERN_WARNING LOGNAME - "MIDI interfaces not configured\n"); - return -1; - } - - /* Route external MIDI to WaveFront synth (by default) */ - - if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "cannot enable MIDI-IN to synth routing.\n"); - /* XXX error ? */ - } - - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - /* Get the regular MIDI patch loading function, so we can - use it if we ever get handed a SYSEX patch. This is - unlikely, because its so damn slow, but we may as well - leave this functionality from maui.c behind, since it - could be useful for sequencer applications that can - only use MIDI to do patch loading. - */ - - if (midi_devs[dev.mididev]->converter != NULL) { - midi_load_patch = midi_devs[dev.mididev]->converter->load_patch; - midi_devs[dev.mididev]->converter->load_patch = - &wavefront_oss_load_patch; - } - -#endif /* OSS_SUPPORT_SEQ */ - - /* Turn on Virtual MIDI, but first *always* turn it off, - since otherwise consectutive reloads of the driver will - never cause the hardware to generate the initial "internal" or - "external" source bytes in the MIDI data stream. This - is pretty important, since the internal hardware generally will - be used to generate none or very little MIDI output, and - thus the only source of MIDI data is actually external. Without - the switch bytes, the driver will think it all comes from - the internal interface. Duh. - */ - - if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "virtual MIDI mode not disabled\n"); - return 0; /* We're OK, but missing the external MIDI dev */ - } - - if ((dev.ext_mididev = virtual_midi_enable ()) < 0) { - printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n"); - } else { - if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) { - printk (KERN_WARNING LOGNAME - "cannot enable virtual MIDI mode.\n"); - virtual_midi_disable (); - } - } - - return 0; -} - -static int __init wavefront_do_reset (int atboot) -{ - char voices[1]; - - if (!atboot && wavefront_hw_reset ()) { - printk (KERN_WARNING LOGNAME "hw reset failed.\n"); - goto gone_bad; - } - - if (dev.israw) { - if (wavefront_download_firmware (ospath)) { - goto gone_bad; - } - - dev.israw = 0; - - /* Wait for the OS to get running. The protocol for - this is non-obvious, and was determined by - using port-IO tracing in DOSemu and some - experimentation here. - - Rather than using timed waits, use interrupts creatively. - */ - - wavefront_should_cause_interrupt (WFC_NOOP, - dev.data_port, - (osrun_time*HZ)); - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "no post-OS interrupt.\n"); - goto gone_bad; - } - - /* Now, do it again ! */ - - wavefront_should_cause_interrupt (WFC_NOOP, - dev.data_port, (10*HZ)); - - if (!dev.irq_ok) { - printk (KERN_WARNING LOGNAME - "no post-OS interrupt(2).\n"); - goto gone_bad; - } - - /* OK, no (RX/TX) interrupts any more, but leave mute - in effect. - */ - - outb (0x80|0x40, dev.control_port); - - /* No need for the IRQ anymore */ - - free_irq (dev.irq, &dev); - - } - - if (dev.has_fx && fx_raw) { - wffx_init (); - } - - /* SETUPSND.EXE asks for sample memory config here, but since i - have no idea how to interpret the result, we'll forget - about it. - */ - - if ((dev.freemem = wavefront_freemem ()) < 0) { - goto gone_bad; - } - - printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024); - - if (wavefront_write (0xf0) || - wavefront_write (1) || - (wavefront_read () < 0)) { - dev.debug = 0; - printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n"); - goto gone_bad; - } - - voices[0] = 32; - - if (wavefront_cmd (WFC_SET_NVOICES, NULL, voices)) { - printk (KERN_WARNING LOGNAME - "cannot set number of voices to 32.\n"); - goto gone_bad; - } - - - return 0; - - gone_bad: - /* reset that sucker so that it doesn't bother us. */ - - outb (0x0, dev.control_port); - dev.interrupts_on = 0; - if (dev.irq >= 0) { - free_irq (dev.irq, &dev); - } - return 1; -} - -static int __init wavefront_init (int atboot) -{ - int samples_are_from_rom; - - if (dev.israw) { - samples_are_from_rom = 1; - } else { - /* XXX is this always true ? */ - samples_are_from_rom = 0; - } - - if (dev.israw || fx_raw) { - if (wavefront_do_reset (atboot)) { - return -1; - } - } - - wavefront_get_sample_status (samples_are_from_rom); - wavefront_get_program_status (); - wavefront_get_patch_status (); - - /* Start normal operation: unreset, master interrupt enabled, no mute - */ - - outb (0x80|0x40|0x20, dev.control_port); - - return (0); -} - -static int __init install_wavefront (void) -{ - if (!request_region (dev.base+2, 6, "wavefront synth")) - return -1; - - if (dev.has_fx) { - if (!request_region (dev.base+8, 8, "wavefront fx")) { - release_region (dev.base+2, 6); - return -1; - } - } - - if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) { - printk (KERN_ERR LOGNAME "cannot register raw synth\n"); - goto err_out; - } - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - if ((dev.oss_dev = sound_alloc_synthdev()) == -1) { - printk (KERN_ERR LOGNAME "Too many sequencers\n"); - /* FIXME: leak: should unregister sound synth */ - goto err_out; - } else { - synth_devs[dev.oss_dev] = &wavefront_operations; - } -#endif /* OSS_SUPPORT_SEQ */ - - if (wavefront_init (1) < 0) { - printk (KERN_WARNING LOGNAME "initialization failed.\n"); - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - sound_unload_synthdev (dev.oss_dev); -#endif /* OSS_SUPPORT_SEQ */ - - goto err_out; - } - - if (wavefront_config_midi ()) { - printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n"); - } - - return dev.oss_dev; - -err_out: - release_region (dev.base+2, 6); - if (dev.has_fx) - release_region (dev.base+8, 8); - return -1; -} - -static void __exit uninstall_wavefront (void) -{ - /* the first two i/o addresses are freed by the wf_mpu code */ - release_region (dev.base+2, 6); - - if (dev.has_fx) { - release_region (dev.base+8, 8); - } - - unregister_sound_synth (dev.synth_dev); - -#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ - sound_unload_synthdev (dev.oss_dev); -#endif /* OSS_SUPPORT_SEQ */ - uninstall_wf_mpu (); -} - -/***********************************************************************/ -/* WaveFront FX control */ -/***********************************************************************/ - -#include "yss225.h" - -/* Control bits for the Load Control Register - */ - -#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ -#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ -#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ - -static int -wffx_idle (void) - -{ - int i; - unsigned int x = 0x80; - - for (i = 0; i < 1000; i++) { - x = inb (dev.fx_status); - if ((x & 0x80) == 0) { - break; - } - } - - if (x & 0x80) { - printk (KERN_ERR LOGNAME "FX device never idle.\n"); - return 0; - } - - return (1); -} - -int __init detect_wffx (void) -{ - /* This is a crude check, but its the best one I have for now. - Certainly on the Maui and the Tropez, wffx_idle() will - report "never idle", which suggests that this test should - work OK. - */ - - if (inb (dev.fx_status) & 0x80) { - printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n"); - return -1; - } - - return 0; -} - -static void -wffx_mute (int onoff) - -{ - if (!wffx_idle()) { - return; - } - - outb (onoff ? 0x02 : 0x00, dev.fx_op); -} - -static int -wffx_memset (int page, - int addr, int cnt, unsigned short *data) -{ - if (page < 0 || page > 7) { - printk (KERN_ERR LOGNAME "FX memset: " - "page must be >= 0 and <= 7\n"); - return -(EINVAL); - } - - if (addr < 0 || addr > 0x7f) { - printk (KERN_ERR LOGNAME "FX memset: " - "addr must be >= 0 and <= 7f\n"); - return -(EINVAL); - } - - if (cnt == 1) { - - outb (FX_LSB_TRANSFER, dev.fx_lcr); - outb (page, dev.fx_dsp_page); - outb (addr, dev.fx_dsp_addr); - outb ((data[0] >> 8), dev.fx_dsp_msb); - outb ((data[0] & 0xff), dev.fx_dsp_lsb); - - printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n", - page, addr, data[0]); - - } else { - int i; - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (page, dev.fx_dsp_page); - outb (addr, dev.fx_dsp_addr); - - for (i = 0; i < cnt; i++) { - outb ((data[i] >> 8), dev.fx_dsp_msb); - outb ((data[i] & 0xff), dev.fx_dsp_lsb); - if (!wffx_idle ()) { - break; - } - } - - if (i != cnt) { - printk (KERN_WARNING LOGNAME - "FX memset " - "(0x%x, 0x%x, %p, %d) incomplete\n", - page, addr, data, cnt); - return -(EIO); - } - } - - return 0; -} - -static int -wffx_ioctl (wavefront_fx_info *r) - -{ - unsigned short page_data[256]; - unsigned short *pd; - - switch (r->request) { - case WFFX_MUTE: - wffx_mute (r->data[0]); - return 0; - - case WFFX_MEMSET: - - if (r->data[2] <= 0) { - printk (KERN_ERR LOGNAME "cannot write " - "<= 0 bytes to FX\n"); - return -(EINVAL); - } else if (r->data[2] == 1) { - pd = (unsigned short *) &r->data[3]; - } else { - if (r->data[2] > sizeof (page_data)) { - printk (KERN_ERR LOGNAME "cannot write " - "> 255 bytes to FX\n"); - return -(EINVAL); - } - if (copy_from_user(page_data, - (unsigned char __user *)r->data[3], - r->data[2])) - return -EFAULT; - pd = page_data; - } - - return wffx_memset (r->data[0], /* page */ - r->data[1], /* addr */ - r->data[2], /* cnt */ - pd); - - default: - printk (KERN_WARNING LOGNAME - "FX: ioctl %d not yet supported\n", - r->request); - return -(EINVAL); - } -} - -/* YSS225 initialization. - - This code was developed using DOSEMU. The Turtle Beach SETUPSND - utility was run with I/O tracing in DOSEMU enabled, and a reconstruction - of the port I/O done, using the Yamaha faxback document as a guide - to add more logic to the code. Its really pretty weird. - - There was an alternative approach of just dumping the whole I/O - sequence as a series of port/value pairs and a simple loop - that output it. However, I hope that eventually I'll get more - control over what this code does, and so I tried to stick with - a somewhat "algorithmic" approach. -*/ - -static int __init wffx_init (void) -{ - int i; - int j; - - /* Set all bits for all channels on the MOD unit to zero */ - /* XXX But why do this twice ? */ - - for (j = 0; j < 2; j++) { - for (i = 0x10; i <= 0xff; i++) { - - if (!wffx_idle ()) { - return (-1); - } - - outb (i, dev.fx_mod_addr); - outb (0x0, dev.fx_mod_data); - } - } - - if (!wffx_idle()) return (-1); - outb (0x02, dev.fx_op); /* mute on */ - - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x44, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x42, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x43, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x7c, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x7e, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x46, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x49, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x47, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x4a, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - - /* either because of stupidity by TB's programmers, or because it - actually does something, rezero the MOD page. - */ - for (i = 0x10; i <= 0xff; i++) { - - if (!wffx_idle ()) { - return (-1); - } - - outb (i, dev.fx_mod_addr); - outb (0x0, dev.fx_mod_data); - } - /* load page zero */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x00, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_zero); i += 2) { - outb (page_zero[i], dev.fx_dsp_msb); - outb (page_zero[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Now load page one */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x01, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_one); i += 2) { - outb (page_one[i], dev.fx_dsp_msb); - outb (page_one[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x02, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_two); i++) { - outb (page_two[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x03, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_three); i++) { - outb (page_three[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x04, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_four); i++) { - outb (page_four[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Load memory area (page six) */ - - outb (FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x06, dev.fx_dsp_page); - - for (i = 0; i < sizeof (page_six); i += 3) { - outb (page_six[i], dev.fx_dsp_addr); - outb (page_six[i+1], dev.fx_dsp_msb); - outb (page_six[i+2], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x07, dev.fx_dsp_page); - outb (0x00, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_seven); i += 2) { - outb (page_seven[i], dev.fx_dsp_msb); - outb (page_seven[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Now setup the MOD area. We do this algorithmically in order to - save a little data space. It could be done in the same fashion - as the "pages". - */ - - for (i = 0x00; i <= 0x0f; i++) { - outb (0x01, dev.fx_mod_addr); - outb (i, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0x02, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0xb0; i <= 0xbf; i++) { - outb (i, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0xf0; i <= 0xff; i++) { - outb (i, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0x10; i <= 0x1d; i++) { - outb (i, dev.fx_mod_addr); - outb (0xff, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x1e, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x1f; i <= 0x2d; i++) { - outb (i, dev.fx_mod_addr); - outb (0xff, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x2e, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x2f; i <= 0x3e; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x3f, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x40; i <= 0x4d; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x4e, dev.fx_mod_addr); - outb (0x0e, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0x4f, dev.fx_mod_addr); - outb (0x0e, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - - for (i = 0x50; i <= 0x6b; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x6c, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - outb (0x6d, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - outb (0x6e, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - outb (0x6f, dev.fx_mod_addr); - outb (0x40, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0x70; i <= 0x7f; i++) { - outb (i, dev.fx_mod_addr); - outb (0xc0, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0x80; i <= 0xaf; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0xc0; i <= 0xdd; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0xde, dev.fx_mod_addr); - outb (0x10, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0xdf, dev.fx_mod_addr); - outb (0x10, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - - for (i = 0xe0; i <= 0xef; i++) { - outb (i, dev.fx_mod_addr); - outb (0x00, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0x00; i <= 0x0f; i++) { - outb (0x01, dev.fx_mod_addr); - outb (i, dev.fx_mod_data); - outb (0x02, dev.fx_mod_addr); - outb (0x01, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - outb (0x02, dev.fx_op); /* mute on */ - - /* Now set the coefficients and so forth for the programs above */ - - for (i = 0; i < sizeof (coefficients); i += 4) { - outb (coefficients[i], dev.fx_dsp_page); - outb (coefficients[i+1], dev.fx_dsp_addr); - outb (coefficients[i+2], dev.fx_dsp_msb); - outb (coefficients[i+3], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - /* Some settings (?) that are too small to bundle into loops */ - - if (!wffx_idle()) return (-1); - outb (0x1e, dev.fx_mod_addr); - outb (0x14, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0xde, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - if (!wffx_idle()) return (-1); - outb (0xdf, dev.fx_mod_addr); - outb (0x20, dev.fx_mod_data); - - /* some more coefficients */ - - if (!wffx_idle()) return (-1); - outb (0x06, dev.fx_dsp_page); - outb (0x78, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x40, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x03, dev.fx_dsp_addr); - outb (0x0f, dev.fx_dsp_msb); - outb (0xff, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x0b, dev.fx_dsp_addr); - outb (0x0f, dev.fx_dsp_msb); - outb (0xff, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x02, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x0a, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x46, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - outb (0x07, dev.fx_dsp_page); - outb (0x49, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - - /* Now, for some strange reason, lets reload every page - and all the coefficients over again. I have *NO* idea - why this is done. I do know that no sound is produced - is this phase is omitted. - */ - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x00, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_zero_v2); i += 2) { - outb (page_zero_v2[i], dev.fx_dsp_msb); - outb (page_zero_v2[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x01, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_one_v2); i += 2) { - outb (page_one_v2[i], dev.fx_dsp_msb); - outb (page_one_v2[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - if (!wffx_idle()) return (-1); - if (!wffx_idle()) return (-1); - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x02, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_two_v2); i++) { - outb (page_two_v2[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x03, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_three_v2); i++) { - outb (page_three_v2[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x04, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_four_v2); i++) { - outb (page_four_v2[i], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x06, dev.fx_dsp_page); - - /* Page six v.2 is algorithmic */ - - for (i = 0x10; i <= 0x3e; i += 2) { - outb (i, dev.fx_dsp_addr); - outb (0x00, dev.fx_dsp_msb); - outb (0x00, dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); - outb (0x07, dev.fx_dsp_page); - outb (0x10, dev.fx_dsp_addr); - - for (i = 0; i < sizeof (page_seven_v2); i += 2) { - outb (page_seven_v2[i], dev.fx_dsp_msb); - outb (page_seven_v2[i+1], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - for (i = 0x00; i < sizeof(mod_v2); i += 2) { - outb (mod_v2[i], dev.fx_mod_addr); - outb (mod_v2[i+1], dev.fx_mod_data); - if (!wffx_idle()) return (-1); - } - - for (i = 0; i < sizeof (coefficients2); i += 4) { - outb (coefficients2[i], dev.fx_dsp_page); - outb (coefficients2[i+1], dev.fx_dsp_addr); - outb (coefficients2[i+2], dev.fx_dsp_msb); - outb (coefficients2[i+3], dev.fx_dsp_lsb); - if (!wffx_idle()) return (-1); - } - - for (i = 0; i < sizeof (coefficients3); i += 2) { - int x; - - outb (0x07, dev.fx_dsp_page); - x = (i % 4) ? 0x4e : 0x4c; - outb (x, dev.fx_dsp_addr); - outb (coefficients3[i], dev.fx_dsp_msb); - outb (coefficients3[i+1], dev.fx_dsp_lsb); - } - - outb (0x00, dev.fx_op); /* mute off */ - if (!wffx_idle()) return (-1); - - return (0); -} - -static int io = -1; -static int irq = -1; - -MODULE_AUTHOR ("Paul Barton-Davis "); -MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver"); -MODULE_LICENSE("GPL"); -module_param (io, int, 0); -module_param (irq, int, 0); - -static int __init init_wavfront (void) -{ - printk ("Turtle Beach WaveFront Driver\n" - "Copyright (C) by Hannu Solvainen, " - "Paul Barton-Davis 1993-1998.\n"); - - /* XXX t'would be lovely to ask the CS4232 for these values, eh ? */ - - if (io == -1 || irq == -1) { - printk (KERN_INFO LOGNAME "irq and io options must be set.\n"); - return -EINVAL; - } - - if (wavefront_interrupt_bits (irq) < 0) { - printk (KERN_INFO LOGNAME - "IRQ must be 9, 5, 12 or 15 (not %d)\n", irq); - return -ENODEV; - } - - if (detect_wavefront (irq, io) < 0) { - return -ENODEV; - } - - if (install_wavefront () < 0) { - return -EIO; - } - - return 0; -} - -static void __exit cleanup_wavfront (void) -{ - uninstall_wavefront (); -} - -module_init(init_wavfront); -module_exit(cleanup_wavfront); diff --git a/sound/oss/wf_midi.c b/sound/oss/wf_midi.c deleted file mode 100644 index 75c0c143a7..0000000000 --- a/sound/oss/wf_midi.c +++ /dev/null @@ -1,880 +0,0 @@ -/* - * sound/oss/wf_midi.c - * - * The low level driver for the WaveFront ICS2115 MIDI interface(s) - * Note that there is also an MPU-401 emulation (actually, a UART-401 - * emulation) on the CS4232 on the Tropez Plus. This code has nothing - * to do with that interface at all. - * - * The interface is essentially just a UART-401, but is has the - * interesting property of supporting what Turtle Beach called - * "Virtual MIDI" mode. In this mode, there are effectively *two* - * MIDI buses accessible via the interface, one that is routed - * solely to/from the external WaveFront synthesizer and the other - * corresponding to the pin/socket connector used to link external - * MIDI devices to the board. - * - * This driver fully supports this mode, allowing two distinct - * midi devices (/dev/midiNN and /dev/midiNN+1) to be used - * completely independently, giving 32 channels of MIDI routing, - * 16 to the WaveFront synth and 16 to the external MIDI bus. - * - * Switching between the two is accomplished externally by the driver - * using the two otherwise unused MIDI bytes. See the code for more details. - * - * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) - * - * The main reason to turn off Virtual MIDI mode is when you want to - * tightly couple the WaveFront synth with an external MIDI - * device. You won't be able to distinguish the source of any MIDI - * data except via SysEx ID, but thats probably OK, since for the most - * part, the WaveFront won't be sending any MIDI data at all. - * - * The main reason to turn on Virtual MIDI Mode is to provide two - * completely independent 16-channel MIDI buses, one to the - * WaveFront and one to any external MIDI devices. Given the 32 - * voice nature of the WaveFront, its pretty easy to find a use - * for all 16 channels driving just that synth. - * - */ - -/* - * Copyright (C) by Paul Barton-Davis 1998 - * Some portions of this file are derived from work that is: - * - * CopyriGht (C) by Hannu Savolainen 1993-1996 - * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include -#include -#include -#include "sound_config.h" - -#include - -#ifdef MODULE - -struct wf_mpu_config { - int base; -#define DATAPORT(d) (d)->base -#define COMDPORT(d) (d)->base+1 -#define STATPORT(d) (d)->base+1 - - int irq; - int opened; - int devno; - int synthno; - int mode; -#define MODE_MIDI 1 -#define MODE_SYNTH 2 - - void (*inputintr) (int dev, unsigned char data); - char isvirtual; /* do virtual I/O stuff */ -}; - -static struct wf_mpu_config devs[2]; -static struct wf_mpu_config *phys_dev = &devs[0]; -static struct wf_mpu_config *virt_dev = &devs[1]; - -static void start_uart_mode (void); -static DEFINE_SPINLOCK(lock); - -#define OUTPUT_READY 0x40 -#define INPUT_AVAIL 0x80 -#define MPU_ACK 0xFE -#define UART_MODE_ON 0x3F - -static inline int wf_mpu_status (void) -{ - return inb (STATPORT (phys_dev)); -} - -static inline int input_avail (void) -{ - return !(wf_mpu_status() & INPUT_AVAIL); -} - -static inline int output_ready (void) -{ - return !(wf_mpu_status() & OUTPUT_READY); -} - -static inline int read_data (void) -{ - return inb (DATAPORT (phys_dev)); -} - -static inline void write_data (unsigned char byte) -{ - outb (byte, DATAPORT (phys_dev)); -} - -/* - * States for the input scanner (should be in dev_table.h) - */ - -#define MST_SYSMSG 100 /* System message (sysx etc). */ -#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ -#define MST_SONGSEL 103 /* Song select */ -#define MST_SONGPOS 104 /* Song position pointer */ -#define MST_TIMED 105 /* Leading timing byte rcvd */ - -/* buffer space check for input scanner */ - -#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ -{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ - mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} - -static unsigned char len_tab[] = /* # of data bytes following a status - */ -{ - 2, /* 8x */ - 2, /* 9x */ - 2, /* Ax */ - 2, /* Bx */ - 1, /* Cx */ - 1, /* Dx */ - 2, /* Ex */ - 0 /* Fx */ -}; - -static int -wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) - -{ - struct midi_input_info *mi = &midi_devs[devno]->in_info; - - switch (mi->m_state) { - case MST_INIT: - switch (midic) { - case 0xf8: - /* Timer overflow */ - break; - - case 0xfc: - break; - - case 0xfd: - /* XXX do something useful with this. If there is - an external MIDI timer (e.g. a hardware sequencer, - a useful timer can be derived ... - - For now, no timer support. - */ - break; - - case 0xfe: - return MPU_ACK; - break; - - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - break; - - case 0xf9: - break; - - case 0xff: - mi->m_state = MST_SYSMSG; - break; - - default: - if (midic <= 0xef) { - mi->m_state = MST_TIMED; - } - else - printk (KERN_ERR " ", - midic); - } - break; - - case MST_TIMED: - { - int msg = ((int) (midic & 0xf0) >> 4); - - mi->m_state = MST_DATA; - - if (msg < 8) { /* Data byte */ - - msg = ((int) (mi->m_prev_status & 0xf0) >> 4); - msg -= 8; - mi->m_left = len_tab[msg] - 1; - - mi->m_ptr = 2; - mi->m_buf[0] = mi->m_prev_status; - mi->m_buf[1] = midic; - - if (mi->m_left <= 0) { - mi->m_state = MST_INIT; - do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); - mi->m_ptr = 0; - } - } else if (msg == 0xf) { /* MPU MARK */ - - mi->m_state = MST_INIT; - - switch (midic) { - case 0xf8: - break; - - case 0xf9: - break; - - case 0xfc: - break; - - default: - break; - } - } else { - mi->m_prev_status = midic; - msg -= 8; - mi->m_left = len_tab[msg]; - - mi->m_ptr = 1; - mi->m_buf[0] = midic; - - if (mi->m_left <= 0) { - mi->m_state = MST_INIT; - do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); - mi->m_ptr = 0; - } - } - } - break; - - case MST_SYSMSG: - switch (midic) { - case 0xf0: - mi->m_state = MST_SYSEX; - break; - - case 0xf1: - mi->m_state = MST_MTC; - break; - - case 0xf2: - mi->m_state = MST_SONGPOS; - mi->m_ptr = 0; - break; - - case 0xf3: - mi->m_state = MST_SONGSEL; - break; - - case 0xf6: - mi->m_state = MST_INIT; - - /* - * Real time messages - */ - case 0xf8: - /* midi clock */ - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xfA: - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xFB: - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xFC: - mi->m_state = MST_INIT; - /* XXX need ext MIDI timer support */ - break; - - case 0xFE: - /* active sensing */ - mi->m_state = MST_INIT; - break; - - case 0xff: - mi->m_state = MST_INIT; - break; - - default: - printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); - mi->m_state = MST_INIT; - } - break; - - case MST_MTC: - mi->m_state = MST_INIT; - break; - - case MST_SYSEX: - if (midic == 0xf7) { - mi->m_state = MST_INIT; - } else { - /* XXX fix me */ - } - break; - - case MST_SONGPOS: - BUFTEST (mi); - mi->m_buf[mi->m_ptr++] = midic; - if (mi->m_ptr == 2) { - mi->m_state = MST_INIT; - mi->m_ptr = 0; - /* XXX need ext MIDI timer support */ - } - break; - - case MST_DATA: - BUFTEST (mi); - mi->m_buf[mi->m_ptr++] = midic; - if ((--mi->m_left) <= 0) { - mi->m_state = MST_INIT; - do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); - mi->m_ptr = 0; - } - break; - - default: - printk (KERN_ERR "Bad state %d ", mi->m_state); - mi->m_state = MST_INIT; - } - - return 1; -} - -static irqreturn_t -wf_mpuintr(int irq, void *dev_id, struct pt_regs *dummy) - -{ - struct wf_mpu_config *physical_dev = dev_id; - static struct wf_mpu_config *input_dev; - struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info; - int n; - - if (!input_avail()) { /* not for us */ - return IRQ_NONE; - } - - if (mi->m_busy) - return IRQ_HANDLED; - spin_lock(&lock); - mi->m_busy = 1; - - if (!input_dev) { - input_dev = physical_dev; - } - - n = 50; /* XXX why ? */ - - do { - unsigned char c = read_data (); - - if (phys_dev->isvirtual) { - - if (c == WF_EXTERNAL_SWITCH) { - input_dev = virt_dev; - continue; - } else if (c == WF_INTERNAL_SWITCH) { - input_dev = phys_dev; - continue; - } /* else just leave it as it is */ - - } else { - input_dev = phys_dev; - } - - if (input_dev->mode == MODE_SYNTH) { - - wf_mpu_input_scanner (input_dev->devno, - input_dev->synthno, c); - - } else if (input_dev->opened & OPEN_READ) { - - if (input_dev->inputintr) { - input_dev->inputintr (input_dev->devno, c); - } - } - - } while (input_avail() && n-- > 0); - - mi->m_busy = 0; - spin_unlock(&lock); - return IRQ_HANDLED; -} - -static int -wf_mpu_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) - ) -{ - struct wf_mpu_config *devc; - - if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) - return -(ENXIO); - - if (phys_dev->devno == dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return -(EINVAL); - } - - if (devc->opened) { - return -(EBUSY); - } - - devc->mode = MODE_MIDI; - devc->opened = mode; - devc->synthno = 0; - - devc->inputintr = input; - return 0; -} - -static void -wf_mpu_close (int dev) -{ - struct wf_mpu_config *devc; - - if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) - return; - - if (phys_dev->devno == dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return; - } - - devc->mode = 0; - devc->inputintr = NULL; - devc->opened = 0; -} - -static int -wf_mpu_out (int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - static int lastoutdev = -1; - unsigned char switchch; - - if (phys_dev->isvirtual && lastoutdev != dev) { - - if (dev == phys_dev->devno) { - switchch = WF_INTERNAL_SWITCH; - } else if (dev == virt_dev->devno) { - switchch = WF_EXTERNAL_SWITCH; - } else { - printk (KERN_ERR "WF-MPU: bad device number %d", dev); - return (0); - } - - /* XXX fix me */ - - for (timeout = 30000; timeout > 0 && !output_ready (); - timeout--); - - spin_lock_irqsave(&lock,flags); - - if (!output_ready ()) { - printk (KERN_WARNING "WF-MPU: Send switch " - "byte timeout\n"); - spin_unlock_irqrestore(&lock,flags); - return 0; - } - - write_data (switchch); - spin_unlock_irqrestore(&lock,flags); - } - - lastoutdev = dev; - - /* - * Sometimes it takes about 30000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - /* XXX fix me */ - - for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); - - spin_lock_irqsave(&lock,flags); - if (!output_ready ()) { - spin_unlock_irqrestore(&lock,flags); - printk (KERN_WARNING "WF-MPU: Send data timeout\n"); - return 0; - } - - write_data (midi_byte); - spin_unlock_irqrestore(&lock,flags); - - return 1; -} - -static inline int wf_mpu_start_read (int dev) { - return 0; -} - -static inline int wf_mpu_end_read (int dev) { - return 0; -} - -static int wf_mpu_ioctl (int dev, unsigned cmd, void __user *arg) -{ - printk (KERN_WARNING - "WF-MPU: Intelligent mode not supported by hardware.\n"); - return -(EINVAL); -} - -static int wf_mpu_buffer_status (int dev) -{ - return 0; -} - -static struct synth_operations wf_mpu_synth_operations[2]; -static struct midi_operations wf_mpu_midi_operations[2]; - -static struct midi_operations wf_mpu_midi_proto = -{ - .owner = THIS_MODULE, - .info = {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, - .in_info = {0}, /* in_info */ - .open = wf_mpu_open, - .close = wf_mpu_close, - .ioctl = wf_mpu_ioctl, - .outputc = wf_mpu_out, - .start_read = wf_mpu_start_read, - .end_read = wf_mpu_end_read, - .buffer_status = wf_mpu_buffer_status, -}; - -static struct synth_info wf_mpu_synth_info_proto = -{"WaveFront MPU-401 interface", 0, - SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; - -static struct synth_info wf_mpu_synth_info[2]; - -static int -wf_mpu_synth_ioctl (int dev, unsigned int cmd, void __user *arg) -{ - int midi_dev; - int index; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) - return -(ENXIO); - - if (midi_dev == phys_dev->devno) { - index = 0; - } else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) { - index = 1; - } else { - return -(EINVAL); - } - - switch (cmd) { - - case SNDCTL_SYNTH_INFO: - if (copy_to_user(arg, - &wf_mpu_synth_info[index], - sizeof (struct synth_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - default: - return -EINVAL; - } -} - -static int -wf_mpu_synth_open (int dev, int mode) -{ - int midi_dev; - struct wf_mpu_config *devc; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { - return -(ENXIO); - } - - if (phys_dev->devno == midi_dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return -(EINVAL); - } - - if (devc->opened) { - return -(EBUSY); - } - - devc->mode = MODE_SYNTH; - devc->synthno = dev; - devc->opened = mode; - devc->inputintr = NULL; - return 0; -} - -static void -wf_mpu_synth_close (int dev) -{ - int midi_dev; - struct wf_mpu_config *devc; - - midi_dev = synth_devs[dev]->midi_dev; - - if (phys_dev->devno == midi_dev) { - devc = phys_dev; - } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { - devc = virt_dev; - } else { - printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); - return; - } - - devc->inputintr = NULL; - devc->opened = 0; - devc->mode = 0; -} - -#define _MIDI_SYNTH_C_ -#define MIDI_SYNTH_NAME "WaveFront (MIDI)" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct synth_operations wf_mpu_synth_proto = -{ - .owner = THIS_MODULE, - .id = "WaveFront (ICS2115)", - .info = NULL, /* info field, filled in during configuration */ - .midi_dev = 0, /* MIDI dev XXX should this be -1 ? */ - .synth_type = SYNTH_TYPE_MIDI, - .synth_subtype = SAMPLE_TYPE_WAVEFRONT, - .open = wf_mpu_synth_open, - .close = wf_mpu_synth_close, - .ioctl = wf_mpu_synth_ioctl, - .kill_note = midi_synth_kill_note, - .start_note = midi_synth_start_note, - .set_instr = midi_synth_set_instr, - .reset = midi_synth_reset, - .hw_control = midi_synth_hw_control, - .load_patch = midi_synth_load_patch, - .aftertouch = midi_synth_aftertouch, - .controller = midi_synth_controller, - .panning = midi_synth_panning, - .bender = midi_synth_bender, - .setup_voice = midi_synth_setup_voice, - .send_sysex = midi_synth_send_sysex -}; - -static int -config_wf_mpu (struct wf_mpu_config *dev) - -{ - int is_external; - char *name; - int index; - - if (dev == phys_dev) { - name = "WaveFront internal MIDI"; - is_external = 0; - index = 0; - memcpy ((char *) &wf_mpu_synth_operations[index], - (char *) &wf_mpu_synth_proto, - sizeof (struct synth_operations)); - } else { - name = "WaveFront external MIDI"; - is_external = 1; - index = 1; - /* no synth operations for an external MIDI interface */ - } - - memcpy ((char *) &wf_mpu_synth_info[dev->devno], - (char *) &wf_mpu_synth_info_proto, - sizeof (struct synth_info)); - - strcpy (wf_mpu_synth_info[index].name, name); - - wf_mpu_synth_operations[index].midi_dev = dev->devno; - wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index]; - - memcpy ((char *) &wf_mpu_midi_operations[index], - (char *) &wf_mpu_midi_proto, - sizeof (struct midi_operations)); - - if (is_external) { - wf_mpu_midi_operations[index].converter = NULL; - } else { - wf_mpu_midi_operations[index].converter = - &wf_mpu_synth_operations[index]; - } - - strcpy (wf_mpu_midi_operations[index].info.name, name); - - midi_devs[dev->devno] = &wf_mpu_midi_operations[index]; - midi_devs[dev->devno]->in_info.m_busy = 0; - midi_devs[dev->devno]->in_info.m_state = MST_INIT; - midi_devs[dev->devno]->in_info.m_ptr = 0; - midi_devs[dev->devno]->in_info.m_left = 0; - midi_devs[dev->devno]->in_info.m_prev_status = 0; - - devs[index].opened = 0; - devs[index].mode = 0; - - return (0); -} - -int virtual_midi_enable (void) - -{ - if ((virt_dev->devno < 0) && - (virt_dev->devno = sound_alloc_mididev()) == -1) { - printk (KERN_ERR - "WF-MPU: too many midi devices detected\n"); - return -1; - } - - config_wf_mpu (virt_dev); - - phys_dev->isvirtual = 1; - return virt_dev->devno; -} - -int -virtual_midi_disable (void) - -{ - unsigned long flags; - - spin_lock_irqsave(&lock,flags); - - wf_mpu_close (virt_dev->devno); - /* no synth on virt_dev, so no need to call wf_mpu_synth_close() */ - phys_dev->isvirtual = 0; - - spin_unlock_irqrestore(&lock,flags); - - return 0; -} - -int __init detect_wf_mpu (int irq, int io_base) -{ - if (!request_region(io_base, 2, "wavefront midi")) { - printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", - io_base); - return -1; - } - - phys_dev->base = io_base; - phys_dev->irq = irq; - phys_dev->devno = -1; - virt_dev->devno = -1; - - return 0; -} - -int __init install_wf_mpu (void) -{ - if ((phys_dev->devno = sound_alloc_mididev()) < 0){ - - printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); - release_region(phys_dev->base, 2); - return -1; - } - - phys_dev->isvirtual = 0; - - if (config_wf_mpu (phys_dev)) { - - printk (KERN_WARNING - "WF-MPU: configuration for MIDI device %d failed\n", - phys_dev->devno); - sound_unload_mididev (phys_dev->devno); - - } - - /* OK, now we're configured to handle an interrupt ... */ - - if (request_irq (phys_dev->irq, wf_mpuintr, IRQF_DISABLED|IRQF_SHARED, - "wavefront midi", phys_dev) < 0) { - - printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", - phys_dev->irq); - return -1; - - } - - /* This being a WaveFront (ICS-2115) emulated MPU-401, we have - to switch it into UART (dumb) mode, because otherwise, it - won't do anything at all. - */ - - start_uart_mode (); - - return phys_dev->devno; -} - -void -uninstall_wf_mpu (void) - -{ - release_region (phys_dev->base, 2); - free_irq (phys_dev->irq, phys_dev); - sound_unload_mididev (phys_dev->devno); - - if (virt_dev->devno >= 0) { - sound_unload_mididev (virt_dev->devno); - } -} - -static void -start_uart_mode (void) - -{ - int ok, i; - unsigned long flags; - - spin_lock_irqsave(&lock,flags); - - /* XXX fix me */ - - for (i = 0; i < 30000 && !output_ready (); i++); - - outb (UART_MODE_ON, COMDPORT(phys_dev)); - - for (ok = 0, i = 50000; i > 0 && !ok; i--) { - if (input_avail ()) { - if (read_data () == MPU_ACK) { - ok = 1; - } - } - } - - spin_unlock_irqrestore(&lock,flags); -} -#endif diff --git a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c deleted file mode 100644 index 6e22472df9..0000000000 --- a/sound/oss/ymfpci.c +++ /dev/null @@ -1,2692 +0,0 @@ -/* - * Copyright 1999 Jaroslav Kysela - * Copyright 2000 Alan Cox - * Copyright 2001 Kai Germaschewski - * Copyright 2002 Pete Zaitcev - * - * Yamaha YMF7xx driver. - * - * This code is a result of high-speed collision - * between ymfpci.c of ALSA and cs46xx.c of Linux. - * -- Pete Zaitcev ; 2000/09/18 - * - * 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. - * - * TODO: - * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). - * - 96KHz playback for DVD - use pitch of 2.0. - * - Retain DMA buffer on close, do not wait the end of frame. - * - Resolve XXX tagged questions. - * - Cannot play 5133Hz. - * - 2001/01/07 Consider if we can remove voice_lock, like so: - * : Allocate/deallocate voices in open/close under semafore. - * : We access voices in interrupt, that only for pcms that open. - * voice_lock around playback_prepare closes interrupts for insane duration. - * - Revisit the way voice_alloc is done - too confusing, overcomplicated. - * Should support various channel types, however. - * - Remove prog_dmabuf from read/write, leave it in open. - * - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with - * native synthesizer through a playback slot. - * - 2001/11/29 ac97_save_state - * Talk to Kai to remove ac97_save_state before it's too late! - * - Second AC97 - * - Restore S/PDIF - Toshibas have it. - * - * Kai used pci_alloc_consistent for DMA buffer, which sounds a little - * unconventional. However, given how small our fragments can be, - * a little uncached access is perhaps better than endless flushing. - * On i386 and other I/O-coherent architectures pci_alloc_consistent - * is entirely harmless. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY -# include "sound_config.h" -# include "mpu401.h" -#endif -#include "ymfpci.h" - -/* - * I do not believe in debug levels as I never can guess what - * part of the code is going to be problematic in the future. - * Don't forget to run your klogd with -c 8. - * - * Example (do not remove): - * #define YMFDBG(fmt, arg...) do{ printk(KERN_DEBUG fmt, ##arg); }while(0) - */ -#define YMFDBGW(fmt, arg...) /* */ /* write counts */ -#define YMFDBGI(fmt, arg...) /* */ /* interrupts */ -#define YMFDBGX(fmt, arg...) /* */ /* ioctl */ - -static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); -static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); -static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice); -static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank); -static int ymf_playback_prepare(struct ymf_state *state); -static int ymf_capture_prepare(struct ymf_state *state); -static struct ymf_state *ymf_state_alloc(ymfpci_t *unit); - -static void ymfpci_aclink_reset(struct pci_dev * pci); -static void ymfpci_disable_dsp(ymfpci_t *unit); -static void ymfpci_download_image(ymfpci_t *codec); -static void ymf_memload(ymfpci_t *unit); - -static DEFINE_SPINLOCK(ymf_devs_lock); -static LIST_HEAD(ymf_devs); - -/* - * constants - */ - -static struct pci_device_id ymf_id_tbl[] = { -#define DEV(dev, data) \ - { PCI_VENDOR_ID_YAMAHA, dev, PCI_ANY_ID, PCI_ANY_ID, 0, 0, \ - (unsigned long)data } - DEV (PCI_DEVICE_ID_YAMAHA_724, "YMF724"), - DEV (PCI_DEVICE_ID_YAMAHA_724F, "YMF724F"), - DEV (PCI_DEVICE_ID_YAMAHA_740, "YMF740"), - DEV (PCI_DEVICE_ID_YAMAHA_740C, "YMF740C"), - DEV (PCI_DEVICE_ID_YAMAHA_744, "YMF744"), - DEV (PCI_DEVICE_ID_YAMAHA_754, "YMF754"), -#undef DEV - { } -}; -MODULE_DEVICE_TABLE(pci, ymf_id_tbl); - -/* - * common I/O routines - */ - -static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val) -{ - writeb(val, codec->reg_area_virt + offset); -} - -static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset) -{ - return readw(codec->reg_area_virt + offset); -} - -static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val) -{ - writew(val, codec->reg_area_virt + offset); -} - -static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset) -{ - return readl(codec->reg_area_virt + offset); -} - -static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val) -{ - writel(val, codec->reg_area_virt + offset); -} - -static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched) -{ - signed long end_time; - u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; - - end_time = jiffies + 3 * (HZ / 4); - do { - if ((ymfpci_readw(codec, reg) & 0x8000) == 0) - return 0; - if (sched) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - } while (end_time - (signed long)jiffies >= 0); - printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n", - secondary, ymfpci_readw(codec, reg)); - return -EBUSY; -} - -static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val) -{ - ymfpci_t *codec = dev->private_data; - u32 cmd; - - spin_lock(&codec->ac97_lock); - /* XXX Do make use of dev->id */ - ymfpci_codec_ready(codec, 0, 0); - cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; - ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd); - spin_unlock(&codec->ac97_lock); -} - -static u16 _ymfpci_codec_read(ymfpci_t *unit, u8 reg) -{ - int i; - - if (ymfpci_codec_ready(unit, 0, 0)) - return ~0; - ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); - if (ymfpci_codec_ready(unit, 0, 0)) - return ~0; - if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) { - for (i = 0; i < 600; i++) - ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); - } - return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); -} - -static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) -{ - ymfpci_t *unit = dev->private_data; - u16 ret; - - spin_lock(&unit->ac97_lock); - ret = _ymfpci_codec_read(unit, reg); - spin_unlock(&unit->ac97_lock); - - return ret; -} - -/* - * Misc routines - */ - -/* - * Calculate the actual sampling rate relatetively to the base clock (48kHz). - */ -static u32 ymfpci_calc_delta(u32 rate) -{ - switch (rate) { - case 8000: return 0x02aaab00; - case 11025: return 0x03accd00; - case 16000: return 0x05555500; - case 22050: return 0x07599a00; - case 32000: return 0x0aaaab00; - case 44100: return 0x0eb33300; - default: return ((rate << 16) / 48000) << 12; - } -} - -static u32 def_rate[8] = { - 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 -}; - -static u32 ymfpci_calc_lpfK(u32 rate) -{ - u32 i; - static u32 val[8] = { - 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, - 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 - }; - - if (rate == 44100) - return 0x40000000; /* FIXME: What's the right value? */ - for (i = 0; i < 8; i++) - if (rate <= def_rate[i]) - return val[i]; - return val[0]; -} - -static u32 ymfpci_calc_lpfQ(u32 rate) -{ - u32 i; - static u32 val[8] = { - 0x35280000, 0x34A70000, 0x32020000, 0x31770000, - 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 - }; - - if (rate == 44100) - return 0x370A0000; - for (i = 0; i < 8; i++) - if (rate <= def_rate[i]) - return val[i]; - return val[0]; -} - -static u32 ymf_calc_lend(u32 rate) -{ - return (rate * YMF_SAMPF) / 48000; -} - -/* - * We ever allow only a few formats, but let's be generic, for smaller surprise. - */ -static int ymf_pcm_format_width(int format) -{ - static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE; - - if ((format & (format-1)) != 0) { - printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format); - return 8; - } - - if (format == AFMT_IMA_ADPCM) return 4; - if ((format & mask16) != 0) return 16; - return 8; -} - -static void ymf_pcm_update_shift(struct ymf_pcm_format *f) -{ - f->shift = 0; - if (f->voices == 2) - f->shift++; - if (ymf_pcm_format_width(f->format) == 16) - f->shift++; -} - -/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */ -#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) -#define DMABUF_MINORDER 1 - -/* - * Allocate DMA buffer - */ -static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) -{ - void *rawbuf = NULL; - dma_addr_t dma_addr; - int order; - struct page *map, *mapend; - - /* alloc as big a chunk as we can */ - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { - rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr); - if (rawbuf) - break; - } - if (!rawbuf) - return -ENOMEM; - -#if 0 - printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n", - PAGE_SIZE << order, order, rawbuf); -#endif - - dmabuf->ready = dmabuf->mapped = 0; - dmabuf->rawbuf = rawbuf; - dmabuf->dma_addr = dma_addr; - dmabuf->buforder = order; - - /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ - mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); - for (map = virt_to_page(rawbuf); map <= mapend; map++) - set_bit(PG_reserved, &map->flags); - - return 0; -} - -/* - * Free DMA buffer - */ -static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) -{ - struct page *map, *mapend; - - if (dmabuf->rawbuf) { - /* undo marking the pages as reserved */ - mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); - for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) - clear_bit(PG_reserved, &map->flags); - - pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder, - dmabuf->rawbuf, dmabuf->dma_addr); - } - dmabuf->rawbuf = NULL; - dmabuf->mapped = dmabuf->ready = 0; -} - -static int prog_dmabuf(struct ymf_state *state, int rec) -{ - struct ymf_dmabuf *dmabuf; - int w_16; - unsigned bufsize; - unsigned long flags; - int redzone, redfrags; - int ret; - - w_16 = ymf_pcm_format_width(state->format.format) == 16; - dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf; - - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->hwptr = dmabuf->swptr = 0; - dmabuf->total_bytes = 0; - dmabuf->count = 0; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - - /* allocate DMA buffer if not allocated yet */ - if (!dmabuf->rawbuf) - if ((ret = alloc_dmabuf(state->unit, dmabuf))) - return ret; - - /* - * Create fake fragment sizes and numbers for OSS ioctls. - * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT. - */ - bufsize = PAGE_SIZE << dmabuf->buforder; - /* By default we give 4 big buffers. */ - dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2); - if (dmabuf->ossfragshift > 3 && - dmabuf->ossfragshift < dmabuf->fragshift) { - /* If OSS set smaller fragments, give more smaller buffers. */ - dmabuf->fragshift = dmabuf->ossfragshift; - } - dmabuf->fragsize = 1 << dmabuf->fragshift; - - dmabuf->numfrag = bufsize >> dmabuf->fragshift; - dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; - - if (dmabuf->ossmaxfrags >= 2) { - redzone = ymf_calc_lend(state->format.rate); - redzone <<= state->format.shift; - redzone *= 3; - redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift; - - if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) { - dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags; - dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; - } - } - - memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); - - /* - * Now set up the ring - */ - - /* XXX ret = rec? cap_pre(): pbk_pre(); */ - spin_lock_irqsave(&state->unit->voice_lock, flags); - if (rec) { - if ((ret = ymf_capture_prepare(state)) != 0) { - spin_unlock_irqrestore(&state->unit->voice_lock, flags); - return ret; - } - } else { - if ((ret = ymf_playback_prepare(state)) != 0) { - spin_unlock_irqrestore(&state->unit->voice_lock, flags); - return ret; - } - } - spin_unlock_irqrestore(&state->unit->voice_lock, flags); - - /* set the ready flag for the dma buffer (this comment is not stupid) */ - dmabuf->ready = 1; - -#if 0 - printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x," - " numfrag %d fragsize %d dmasize %d\n", - state->format.rate, state->format.format, dmabuf->numfrag, - dmabuf->fragsize, dmabuf->dmasize); -#endif - - return 0; -} - -static void ymf_start_dac(struct ymf_state *state) -{ - ymf_playback_trigger(state->unit, &state->wpcm, 1); -} - -// static void ymf_start_adc(struct ymf_state *state) -// { -// ymf_capture_trigger(state->unit, &state->rpcm, 1); -// } - -/* - * Wait until output is drained. - * This does not kill the hardware for the sake of ioctls. - */ -static void ymf_wait_dac(struct ymf_state *state) -{ - struct ymf_unit *unit = state->unit; - struct ymf_pcm *ypcm = &state->wpcm; - DECLARE_WAITQUEUE(waita, current); - unsigned long flags; - - add_wait_queue(&ypcm->dmabuf.wait, &waita); - - spin_lock_irqsave(&unit->reg_lock, flags); - if (ypcm->dmabuf.count != 0 && !ypcm->running) { - ymf_playback_trigger(unit, ypcm, 1); - } - -#if 0 - if (file->f_flags & O_NONBLOCK) { - /* - * XXX Our mistake is to attach DMA buffer to state - * rather than to some per-device structure. - * Cannot skip waiting, can only make it shorter. - */ - } -#endif - - set_current_state(TASK_UNINTERRUPTIBLE); - while (ypcm->running) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - schedule(); - spin_lock_irqsave(&unit->reg_lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - } - spin_unlock_irqrestore(&unit->reg_lock, flags); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&ypcm->dmabuf.wait, &waita); - - /* - * This function may take up to 4 seconds to reach this point - * (32K circular buffer, 8000 Hz). User notices. - */ -} - -/* Can just stop, without wait. Or can we? */ -static void ymf_stop_adc(struct ymf_state *state) -{ - struct ymf_unit *unit = state->unit; - unsigned long flags; - - spin_lock_irqsave(&unit->reg_lock, flags); - ymf_capture_trigger(unit, &state->rpcm, 0); - spin_unlock_irqrestore(&unit->reg_lock, flags); -} - -/* - * Hardware start management - */ - -static void ymfpci_hw_start(ymfpci_t *unit) -{ - unsigned long flags; - - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->start_count++ == 0) { - ymfpci_writel(unit, YDSXGR_MODE, - ymfpci_readl(unit, YDSXGR_MODE) | 3); - unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; - } - spin_unlock_irqrestore(&unit->reg_lock, flags); -} - -static void ymfpci_hw_stop(ymfpci_t *unit) -{ - unsigned long flags; - long timeout = 1000; - - spin_lock_irqsave(&unit->reg_lock, flags); - if (--unit->start_count == 0) { - ymfpci_writel(unit, YDSXGR_MODE, - ymfpci_readl(unit, YDSXGR_MODE) & ~3); - while (timeout-- > 0) { - if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) - break; - } - } - spin_unlock_irqrestore(&unit->reg_lock, flags); -} - -/* - * Playback voice management - */ - -static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[]) -{ - ymfpci_voice_t *voice, *voice2; - int idx; - - for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { - voice = &codec->voices[idx]; - voice2 = pair ? &codec->voices[idx+1] : NULL; - if (voice->use || (voice2 && voice2->use)) - continue; - voice->use = 1; - if (voice2) - voice2->use = 1; - switch (type) { - case YMFPCI_PCM: - voice->pcm = 1; - if (voice2) - voice2->pcm = 1; - break; - case YMFPCI_SYNTH: - voice->synth = 1; - break; - case YMFPCI_MIDI: - voice->midi = 1; - break; - } - ymfpci_hw_start(codec); - rvoice[0] = voice; - if (voice2) { - ymfpci_hw_start(codec); - rvoice[1] = voice2; - } - return 0; - } - return -EBUSY; /* Your audio channel is open by someone else. */ -} - -static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice) -{ - ymfpci_hw_stop(unit); - pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; - pvoice->ypcm = NULL; -} - -/* - */ - -static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) -{ - struct ymf_pcm *ypcm; - int redzone; - int pos, delta, swptr; - int played, distance; - struct ymf_state *state; - struct ymf_dmabuf *dmabuf; - char silence; - - if ((ypcm = voice->ypcm) == NULL) { - return; - } - if ((state = ypcm->state) == NULL) { - ypcm->running = 0; // lock it - return; - } - dmabuf = &ypcm->dmabuf; - spin_lock(&codec->reg_lock); - if (ypcm->running) { - YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", - voice->number, codec->active_bank, dmabuf->count, - le32_to_cpu(voice->bank[0].start), - le32_to_cpu(voice->bank[1].start)); - silence = (ymf_pcm_format_width(state->format.format) == 16) ? - 0 : 0x80; - /* We need actual left-hand-side redzone size here. */ - redzone = ymf_calc_lend(state->format.rate); - redzone <<= (state->format.shift + 1); - swptr = dmabuf->swptr; - - pos = le32_to_cpu(voice->bank[codec->active_bank].start); - pos <<= state->format.shift; - if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ - printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n", - codec->dev_audio, voice->number, - dmabuf->hwptr, pos, dmabuf->dmasize); - pos = 0; - } - if (pos < dmabuf->hwptr) { - delta = dmabuf->dmasize - dmabuf->hwptr; - memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); - delta += pos; - memset(dmabuf->rawbuf, silence, pos); - } else { - delta = pos - dmabuf->hwptr; - memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); - } - dmabuf->hwptr = pos; - - if (dmabuf->count == 0) { - printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n", - codec->dev_audio, voice->number, dmabuf->hwptr); - ymf_playback_trigger(codec, ypcm, 0); - } - - if (swptr <= pos) { - distance = pos - swptr; - } else { - distance = dmabuf->dmasize - (swptr - pos); - } - if (distance < redzone) { - /* - * hwptr inside redzone => DMA ran out of samples. - */ - if (delta < dmabuf->count) { - /* - * Lost interrupt or other screwage. - */ - printk(KERN_ERR "ymfpci%d: %d: lost: delta %d" - " hwptr %d swptr %d distance %d count %d\n", - codec->dev_audio, voice->number, delta, - dmabuf->hwptr, swptr, distance, dmabuf->count); - } else { - /* - * Normal end of DMA. - */ - YMFDBGI("ymfpci%d: %d: done: delta %d" - " hwptr %d swptr %d distance %d count %d\n", - codec->dev_audio, voice->number, delta, - dmabuf->hwptr, swptr, distance, dmabuf->count); - } - played = dmabuf->count; - if (ypcm->running) { - ymf_playback_trigger(codec, ypcm, 0); - } - } else { - /* - * hwptr is chipping away towards a remote swptr. - * Calculate other distance and apply it to count. - */ - if (swptr >= pos) { - distance = swptr - pos; - } else { - distance = dmabuf->dmasize - (pos - swptr); - } - if (distance < dmabuf->count) { - played = dmabuf->count - distance; - } else { - played = 0; - } - } - - dmabuf->total_bytes += played; - dmabuf->count -= played; - if (dmabuf->count < dmabuf->dmasize / 2) { - wake_up(&dmabuf->wait); - } - } - spin_unlock(&codec->reg_lock); -} - -static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap) -{ - struct ymf_pcm *ypcm; - int redzone; - struct ymf_state *state; - struct ymf_dmabuf *dmabuf; - int pos, delta; - int cnt; - - if ((ypcm = cap->ypcm) == NULL) { - return; - } - if ((state = ypcm->state) == NULL) { - ypcm->running = 0; // lock it - return; - } - dmabuf = &ypcm->dmabuf; - spin_lock(&unit->reg_lock); - if (ypcm->running) { - redzone = ymf_calc_lend(state->format.rate); - redzone <<= (state->format.shift + 1); - - pos = le32_to_cpu(cap->bank[unit->active_bank].start); - // pos <<= state->format.shift; - if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ - printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n", - unit->dev_audio, ypcm->capture_bank_number, - dmabuf->hwptr, pos, dmabuf->dmasize); - pos = 0; - } - if (pos < dmabuf->hwptr) { - delta = dmabuf->dmasize - dmabuf->hwptr; - delta += pos; - } else { - delta = pos - dmabuf->hwptr; - } - dmabuf->hwptr = pos; - - cnt = dmabuf->count; - cnt += delta; - if (cnt + redzone > dmabuf->dmasize) { - /* Overflow - bump swptr */ - dmabuf->count = dmabuf->dmasize - redzone; - dmabuf->swptr = dmabuf->hwptr + redzone; - if (dmabuf->swptr >= dmabuf->dmasize) { - dmabuf->swptr -= dmabuf->dmasize; - } - } else { - dmabuf->count = cnt; - } - - dmabuf->total_bytes += delta; - if (dmabuf->count) { /* && is_sleeping XXX */ - wake_up(&dmabuf->wait); - } - } - spin_unlock(&unit->reg_lock); -} - -static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) -{ - - if (ypcm->voices[0] == NULL) { - return -EINVAL; - } - if (cmd != 0) { - codec->ctrl_playback[ypcm->voices[0]->number + 1] = - cpu_to_le32(ypcm->voices[0]->bank_ba); - if (ypcm->voices[1] != NULL) - codec->ctrl_playback[ypcm->voices[1]->number + 1] = - cpu_to_le32(ypcm->voices[1]->bank_ba); - ypcm->running = 1; - } else { - codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; - if (ypcm->voices[1] != NULL) - codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; - ypcm->running = 0; - } - return 0; -} - -static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) -{ - u32 tmp; - - if (cmd != 0) { - tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); - ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); - ypcm->running = 1; - } else { - tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); - ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); - ypcm->running = 0; - } -} - -static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices) -{ - struct ymf_unit *unit; - int err; - - unit = ypcm->state->unit; - if (ypcm->voices[1] != NULL && voices < 2) { - ymfpci_voice_free(unit, ypcm->voices[1]); - ypcm->voices[1] = NULL; - } - if (voices == 1 && ypcm->voices[0] != NULL) - return 0; /* already allocated */ - if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) - return 0; /* already allocated */ - if (voices > 1) { - if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { - ymfpci_voice_free(unit, ypcm->voices[0]); - ypcm->voices[0] = NULL; - } - if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0) - return err; - ypcm->voices[0]->ypcm = ypcm; - ypcm->voices[1]->ypcm = ypcm; - } else { - if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0) - return err; - ypcm->voices[0]->ypcm = ypcm; - } - return 0; -} - -static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, - int rate, int w_16, unsigned long addr, unsigned int end, int spdif) -{ - u32 format; - u32 delta = ymfpci_calc_delta(rate); - u32 lpfQ = ymfpci_calc_lpfQ(rate); - u32 lpfK = ymfpci_calc_lpfK(rate); - ymfpci_playback_bank_t *bank; - int nbank; - - /* - * The gain is a floating point number. According to the manual, - * bit 31 indicates a sign bit, bit 30 indicates an integer part, - * and bits [29:15] indicate a decimal fraction part. Thus, - * for a gain of 1.0 the constant of 0x40000000 is loaded. - */ - unsigned default_gain = cpu_to_le32(0x40000000); - - format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); - if (stereo) - end >>= 1; - if (w_16) - end >>= 1; - for (nbank = 0; nbank < 2; nbank++) { - bank = &voice->bank[nbank]; - bank->format = cpu_to_le32(format); - bank->loop_default = 0; /* 0-loops forever, otherwise count */ - bank->base = cpu_to_le32(addr); - bank->loop_start = 0; - bank->loop_end = cpu_to_le32(end); - bank->loop_frac = 0; - bank->eg_gain_end = default_gain; - bank->lpfQ = cpu_to_le32(lpfQ); - bank->status = 0; - bank->num_of_frames = 0; - bank->loop_count = 0; - bank->start = 0; - bank->start_frac = 0; - bank->delta = - bank->delta_end = cpu_to_le32(delta); - bank->lpfK = - bank->lpfK_end = cpu_to_le32(lpfK); - bank->eg_gain = default_gain; - bank->lpfD1 = - bank->lpfD2 = 0; - - bank->left_gain = - bank->right_gain = - bank->left_gain_end = - bank->right_gain_end = - bank->eff1_gain = - bank->eff2_gain = - bank->eff3_gain = - bank->eff1_gain_end = - bank->eff2_gain_end = - bank->eff3_gain_end = 0; - - if (!stereo) { - if (!spdif) { - bank->left_gain = - bank->right_gain = - bank->left_gain_end = - bank->right_gain_end = default_gain; - } else { - bank->eff2_gain = - bank->eff2_gain_end = - bank->eff3_gain = - bank->eff3_gain_end = default_gain; - } - } else { - if (!spdif) { - if ((voice->number & 1) == 0) { - bank->left_gain = - bank->left_gain_end = default_gain; - } else { - bank->format |= cpu_to_le32(1); - bank->right_gain = - bank->right_gain_end = default_gain; - } - } else { - if ((voice->number & 1) == 0) { - bank->eff2_gain = - bank->eff2_gain_end = default_gain; - } else { - bank->format |= cpu_to_le32(1); - bank->eff3_gain = - bank->eff3_gain_end = default_gain; - } - } - } - } -} - -/* - * XXX Capture channel allocation is entirely fake at the moment. - * We use only one channel and mark it busy as required. - */ -static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank) -{ - struct ymf_capture *cap; - int cbank; - - cbank = 1; /* Only ADC slot is used for now. */ - cap = &unit->capture[cbank]; - if (cap->use) - return -EBUSY; - cap->use = 1; - *pbank = cbank; - return 0; -} - -static int ymf_playback_prepare(struct ymf_state *state) -{ - struct ymf_pcm *ypcm = &state->wpcm; - int err, nvoice; - - if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { - /* Somebody started 32 mpg123's in parallel? */ - printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", - state->unit->dev_audio); - return err; - } - - for (nvoice = 0; nvoice < state->format.voices; nvoice++) { - ymf_pcm_init_voice(ypcm->voices[nvoice], - state->format.voices == 2, state->format.rate, - ymf_pcm_format_width(state->format.format) == 16, - ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize, - ypcm->spdif); - } - return 0; -} - -static int ymf_capture_prepare(struct ymf_state *state) -{ - ymfpci_t *unit = state->unit; - struct ymf_pcm *ypcm = &state->rpcm; - ymfpci_capture_bank_t * bank; - /* XXX This is confusing, gotta rename one of them banks... */ - int nbank; /* flip-flop bank */ - int cbank; /* input [super-]bank */ - struct ymf_capture *cap; - u32 rate, format; - - if (ypcm->capture_bank_number == -1) { - if (ymf_capture_alloc(unit, &cbank) != 0) - return -EBUSY; - - ypcm->capture_bank_number = cbank; - - cap = &unit->capture[cbank]; - cap->bank = unit->bank_capture[cbank][0]; - cap->ypcm = ypcm; - ymfpci_hw_start(unit); - } - - // ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream); - // frag_size is replaced with nonfragged byte-aligned rolling buffer - rate = ((48000 * 4096) / state->format.rate) - 1; - format = 0; - if (state->format.voices == 2) - format |= 2; - if (ymf_pcm_format_width(state->format.format) == 8) - format |= 1; - switch (ypcm->capture_bank_number) { - case 0: - ymfpci_writel(unit, YDSXGR_RECFORMAT, format); - ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate); - break; - case 1: - ymfpci_writel(unit, YDSXGR_ADCFORMAT, format); - ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate); - break; - } - for (nbank = 0; nbank < 2; nbank++) { - bank = unit->bank_capture[ypcm->capture_bank_number][nbank]; - bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr); - // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift; - bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize); - bank->start = 0; - bank->num_of_loops = 0; - } -#if 0 /* s/pdif */ - if (state->digital.dig_valid) - /*state->digital.type == SND_PCM_DIG_AES_IEC958*/ - ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, - state->digital.dig_status[0] | (state->digital.dig_status[1] << 8)); -#endif - return 0; -} - -static irqreturn_t ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - ymfpci_t *codec = dev_id; - u32 status, nvoice, mode; - struct ymf_voice *voice; - struct ymf_capture *cap; - - status = ymfpci_readl(codec, YDSXGR_STATUS); - if (status & 0x80000000) { - codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; - spin_lock(&codec->voice_lock); - for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { - voice = &codec->voices[nvoice]; - if (voice->use) - ymf_pcm_interrupt(codec, voice); - } - for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { - cap = &codec->capture[nvoice]; - if (cap->use) - ymf_cap_interrupt(codec, cap); - } - spin_unlock(&codec->voice_lock); - spin_lock(&codec->reg_lock); - ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000); - mode = ymfpci_readl(codec, YDSXGR_MODE) | 2; - ymfpci_writel(codec, YDSXGR_MODE, mode); - spin_unlock(&codec->reg_lock); - } - - status = ymfpci_readl(codec, YDSXGR_INTFLAG); - if (status & 1) { - /* timer handler */ - ymfpci_writel(codec, YDSXGR_INTFLAG, ~0); - } - return IRQ_HANDLED; -} - -static void ymf_pcm_free_substream(struct ymf_pcm *ypcm) -{ - unsigned long flags; - struct ymf_unit *unit; - - unit = ypcm->state->unit; - - if (ypcm->type == PLAYBACK_VOICE) { - spin_lock_irqsave(&unit->voice_lock, flags); - if (ypcm->voices[1]) - ymfpci_voice_free(unit, ypcm->voices[1]); - if (ypcm->voices[0]) - ymfpci_voice_free(unit, ypcm->voices[0]); - spin_unlock_irqrestore(&unit->voice_lock, flags); - } else { - if (ypcm->capture_bank_number != -1) { - unit->capture[ypcm->capture_bank_number].use = 0; - ypcm->capture_bank_number = -1; - ymfpci_hw_stop(unit); - } - } -} - -static struct ymf_state *ymf_state_alloc(ymfpci_t *unit) -{ - struct ymf_pcm *ypcm; - struct ymf_state *state; - - if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) { - goto out0; - } - memset(state, 0, sizeof(struct ymf_state)); - - ypcm = &state->wpcm; - ypcm->state = state; - ypcm->type = PLAYBACK_VOICE; - ypcm->capture_bank_number = -1; - init_waitqueue_head(&ypcm->dmabuf.wait); - - ypcm = &state->rpcm; - ypcm->state = state; - ypcm->type = CAPTURE_AC97; - ypcm->capture_bank_number = -1; - init_waitqueue_head(&ypcm->dmabuf.wait); - - state->unit = unit; - - state->format.format = AFMT_U8; - state->format.rate = 8000; - state->format.voices = 1; - ymf_pcm_update_shift(&state->format); - - return state; - -out0: - return NULL; -} - -/* AES/IEC958 channel status bits */ -#define SND_PCM_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ -#define SND_PCM_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ -#define SND_PCM_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ -#define SND_PCM_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ -#define SND_PCM_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ -#define SND_PCM_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ -#define SND_PCM_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ -#define SND_PCM_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ -#define SND_PCM_AES0_PRO_FS (3<<6) /* mask - sample frequency */ -#define SND_PCM_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ -#define SND_PCM_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ -#define SND_PCM_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ -#define SND_PCM_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ -#define SND_PCM_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ -#define SND_PCM_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ -#define SND_PCM_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ -#define SND_PCM_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ -#define SND_PCM_AES0_CON_MODE (3<<6) /* mask - mode */ -#define SND_PCM_AES1_PRO_MODE (15<<0) /* mask - channel mode */ -#define SND_PCM_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ -#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ -#define SND_PCM_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ -#define SND_PCM_AES1_PRO_MODE_TWO (8<<0) /* two channels */ -#define SND_PCM_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ -#define SND_PCM_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ -#define SND_PCM_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ -#define SND_PCM_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ -#define SND_PCM_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ -#define SND_PCM_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ -#define SND_PCM_AES1_CON_CATEGORY 0x7f -#define SND_PCM_AES1_CON_GENERAL 0x00 -#define SND_PCM_AES1_CON_EXPERIMENTAL 0x40 -#define SND_PCM_AES1_CON_SOLIDMEM_MASK 0x0f -#define SND_PCM_AES1_CON_SOLIDMEM_ID 0x08 -#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07 -#define SND_PCM_AES1_CON_BROADCAST1_ID 0x04 -#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07 -#define SND_PCM_AES1_CON_DIGDIGCONV_ID 0x02 -#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f -#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06 -#define SND_PCM_AES1_CON_ADC_MASK 0x1f -#define SND_PCM_AES1_CON_ADC_ID 0x16 -#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f -#define SND_PCM_AES1_CON_BROADCAST2_ID 0x0e -#define SND_PCM_AES1_CON_LASEROPT_MASK 0x07 -#define SND_PCM_AES1_CON_LASEROPT_ID 0x01 -#define SND_PCM_AES1_CON_MUSICAL_MASK 0x07 -#define SND_PCM_AES1_CON_MUSICAL_ID 0x05 -#define SND_PCM_AES1_CON_MAGNETIC_MASK 0x07 -#define SND_PCM_AES1_CON_MAGNETIC_ID 0x03 -#define SND_PCM_AES1_CON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x00) -#define SND_PCM_AES1_CON_NON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x08) -#define SND_PCM_AES1_CON_PCM_CODER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00) -#define SND_PCM_AES1_CON_SAMPLER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20) -#define SND_PCM_AES1_CON_MIXER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10) -#define SND_PCM_AES1_CON_RATE_CONVERTER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18) -#define SND_PCM_AES1_CON_SYNTHESIZER (SND_PCM_AES1_CON_MUSICAL_ID|0x00) -#define SND_PCM_AES1_CON_MICROPHONE (SND_PCM_AES1_CON_MUSICAL_ID|0x08) -#define SND_PCM_AES1_CON_DAT (SND_PCM_AES1_CON_MAGNETIC_ID|0x00) -#define SND_PCM_AES1_CON_VCR (SND_PCM_AES1_CON_MAGNETIC_ID|0x08) -#define SND_PCM_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ -#define SND_PCM_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ -#define SND_PCM_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ -#define SND_PCM_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ -#define SND_PCM_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ -#define SND_PCM_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ -#define SND_PCM_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ -#define SND_PCM_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ -#define SND_PCM_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ -#define SND_PCM_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ -#define SND_PCM_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ -#define SND_PCM_AES2_CON_SOURCE (15<<0) /* mask - source number */ -#define SND_PCM_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ -#define SND_PCM_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ -#define SND_PCM_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ -#define SND_PCM_AES3_CON_FS (15<<0) /* mask - sample frequency */ -#define SND_PCM_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ -#define SND_PCM_AES3_CON_FS_48000 (2<<0) /* 48kHz */ -#define SND_PCM_AES3_CON_FS_32000 (3<<0) /* 32kHz */ -#define SND_PCM_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ -#define SND_PCM_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ -#define SND_PCM_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ -#define SND_PCM_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ - -/* - * User interface - */ - -/* - * in this loop, dmabuf.count signifies the amount of data that is - * waiting to be copied to the user's buffer. it is filled by the dma - * machine and drained by this loop. - */ -static ssize_t -ymf_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf; - struct ymf_unit *unit = state->unit; - DECLARE_WAITQUEUE(waita, current); - ssize_t ret; - unsigned long flags; - unsigned int swptr; - int cnt; /* This many to go in this revolution */ - - if (dmabuf->mapped) - return -ENXIO; - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - return ret; - ret = 0; - - add_wait_queue(&dmabuf->wait, &waita); - set_current_state(TASK_INTERRUPTIBLE); - while (count > 0) { - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - if (!ret) ret = -EAGAIN; - break; - } - continue; - } - swptr = dmabuf->swptr; - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count < cnt) - cnt = dmabuf->count; - spin_unlock_irqrestore(&unit->reg_lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - unsigned long tmo; - /* buffer is empty, start the dma machine and wait for data to be - recorded */ - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (!state->rpcm.running) { - ymf_capture_trigger(state->unit, &state->rpcm, 1); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - break; - } - /* This isnt strictly right for the 810 but it'll do */ - tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2); - tmo >>= state->format.shift; - /* There are two situations when sleep_on_timeout returns, one is when - the interrupt is serviced correctly and the process is waked up by - ISR ON TIME. Another is when timeout is expired, which means that - either interrupt is NOT serviced correctly (pending interrupt) or it - is TOO LATE for the process to be scheduled to run (scheduler latency) - which results in a (potential) buffer overrun. And worse, there is - NOTHING we can do to prevent it. */ - tmo = schedule_timeout(tmo); - spin_lock_irqsave(&state->unit->reg_lock, flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tmo == 0 && dmabuf->count == 0) { - printk(KERN_ERR "ymfpci%d: recording schedule timeout, " - "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - state->unit->dev_audio, - dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, - dmabuf->hwptr, dmabuf->swptr); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - break; - } - continue; - } - - if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { - if (!ret) ret = -EFAULT; - break; - } - - swptr = (swptr + cnt) % dmabuf->dmasize; - - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - continue; - } - - dmabuf->swptr = swptr; - dmabuf->count -= cnt; - // spin_unlock_irqrestore(&unit->reg_lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - // spin_lock_irqsave(&unit->reg_lock, flags); - if (!state->rpcm.running) { - ymf_capture_trigger(unit, &state->rpcm, 1); - } - spin_unlock_irqrestore(&unit->reg_lock, flags); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &waita); - - return ret; -} - -static ssize_t -ymf_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; - struct ymf_unit *unit = state->unit; - DECLARE_WAITQUEUE(waita, current); - ssize_t ret; - unsigned long flags; - unsigned int swptr; - int cnt; /* This many to go in this revolution */ - int redzone; - int delay; - - YMFDBGW("ymf_write: count %d\n", count); - - if (dmabuf->mapped) - return -ENXIO; - if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - return ret; - ret = 0; - - /* - * Alan's cs46xx works without a red zone - marvel of ingenuity. - * We are not so brilliant... Red zone does two things: - * 1. allows for safe start after a pause as we have no way - * to know what the actual, relentlessly advancing, hwptr is. - * 2. makes computations in ymf_pcm_interrupt simpler. - */ - redzone = ymf_calc_lend(state->format.rate) << state->format.shift; - redzone *= 3; /* 2 redzone + 1 possible uncertainty reserve. */ - - add_wait_queue(&dmabuf->wait, &waita); - set_current_state(TASK_INTERRUPTIBLE); - while (count > 0) { - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - if (!ret) ret = -EAGAIN; - break; - } - continue; - } - if (dmabuf->count < 0) { - printk(KERN_ERR - "ymf_write: count %d, was legal in cs46xx\n", - dmabuf->count); - dmabuf->count = 0; - } - if (dmabuf->count == 0) { - swptr = dmabuf->hwptr; - if (state->wpcm.running) { - /* - * Add uncertainty reserve. - */ - cnt = ymf_calc_lend(state->format.rate); - cnt <<= state->format.shift; - if ((swptr += cnt) >= dmabuf->dmasize) { - swptr -= dmabuf->dmasize; - } - } - dmabuf->swptr = swptr; - } else { - /* - * XXX This is not right if dmabuf->count is small - - * about 2*x frame size or less. We cannot count on - * on appending and not causing an artefact. - * Should use a variation of the count==0 case above. - */ - swptr = dmabuf->swptr; - } - cnt = dmabuf->dmasize - swptr; - if (dmabuf->count + cnt > dmabuf->dmasize - redzone) - cnt = (dmabuf->dmasize - redzone) - dmabuf->count; - spin_unlock_irqrestore(&unit->reg_lock, flags); - - if (cnt > count) - cnt = count; - if (cnt <= 0) { - YMFDBGW("ymf_write: full, count %d swptr %d\n", - dmabuf->count, dmabuf->swptr); - /* - * buffer is full, start the dma machine and - * wait for data to be played - */ - spin_lock_irqsave(&unit->reg_lock, flags); - if (!state->wpcm.running) { - ymf_playback_trigger(unit, &state->wpcm, 1); - } - spin_unlock_irqrestore(&unit->reg_lock, flags); - if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; - break; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - if (!ret) ret = -ERESTARTSYS; - break; - } - continue; - } - if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; - break; - } - - if ((swptr += cnt) >= dmabuf->dmasize) { - swptr -= dmabuf->dmasize; - } - - spin_lock_irqsave(&unit->reg_lock, flags); - if (unit->suspended) { - spin_unlock_irqrestore(&unit->reg_lock, flags); - continue; - } - dmabuf->swptr = swptr; - dmabuf->count += cnt; - - /* - * Start here is a bad idea - may cause startup click - * in /bin/play when dmabuf is not full yet. - * However, some broken applications do not make - * any use of SNDCTL_DSP_SYNC (Doom is the worst). - * One frame is about 5.3ms, Doom write size is 46ms. - */ - delay = state->format.rate / 20; /* 50ms */ - delay <<= state->format.shift; - if (dmabuf->count >= delay && !state->wpcm.running) { - ymf_playback_trigger(unit, &state->wpcm, 1); - } - - spin_unlock_irqrestore(&unit->reg_lock, flags); - - count -= cnt; - buffer += cnt; - ret += cnt; - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&dmabuf->wait, &waita); - - YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, dmabuf->count); - return ret; -} - -static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf; - int redzone; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_mode & FMODE_WRITE) - poll_wait(file, &state->wpcm.dmabuf.wait, wait); - if (file->f_mode & FMODE_READ) - poll_wait(file, &state->rpcm.dmabuf.wait, wait); - - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (file->f_mode & FMODE_READ) { - dmabuf = &state->rpcm.dmabuf; - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - redzone = ymf_calc_lend(state->format.rate); - redzone <<= state->format.shift; - redzone *= 3; - - dmabuf = &state->wpcm.dmabuf; - if (dmabuf->mapped) { - if (dmabuf->count >= (signed)dmabuf->fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - /* - * Don't select unless a full fragment is available. - * Otherwise artsd does GETOSPACE, sees 0, and loops. - */ - if (dmabuf->count + redzone + dmabuf->fragsize - <= dmabuf->dmasize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - - return mask; -} - -static int ymf_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; - int ret; - unsigned long size; - - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(state, 0)) != 0) - return ret; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(state, 1)) != 0) - return ret; - } else - return -EINVAL; - - if (vma->vm_pgoff != 0) - return -EINVAL; - size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << dmabuf->buforder)) - return -EINVAL; - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - return -EAGAIN; - dmabuf->mapped = 1; - -/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n"); - return 0; -} - -static int ymf_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - struct ymf_dmabuf *dmabuf; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int redzone; - int val; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - switch (cmd) { - case OSS_GETVERSION: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg); - return put_user(SOUND_VERSION, p); - - case SNDCTL_DSP_RESET: - YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd); - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = dmabuf->total_bytes = 0; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - dmabuf->swptr = dmabuf->hwptr; - dmabuf->count = dmabuf->total_bytes = 0; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - return 0; - - case SNDCTL_DSP_SYNC: - YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd); - if (file->f_mode & FMODE_WRITE) { - dmabuf = &state->wpcm.dmabuf; - if (file->f_flags & O_NONBLOCK) { - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (dmabuf->count != 0 && !state->wpcm.running) { - ymf_start_dac(state); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } else { - ymf_wait_dac(state); - } - } - /* XXX What does this do for reading? dmabuf->count=0; ? */ - return 0; - - case SNDCTL_DSP_SPEED: /* set smaple rate */ - if (get_user(val, p)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val); - if (val >= 8000 && val <= 48000) { - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.rate = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.rate = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - return put_user(state->format.rate, p); - - /* - * OSS manual does not mention SNDCTL_DSP_STEREO at all. - * All channels are mono and if you want stereo, you - * play into two channels with SNDCTL_DSP_CHANNELS. - * However, mpg123 calls it. I wonder, why Michael Hipp used it. - */ - case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ - if (get_user(val, p)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val); - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.voices = val ? 2 : 1; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.voices = val ? 2 : 1; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd); - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(state, 0))) - return val; - val = state->wpcm.dmabuf.fragsize; - YMFDBGX("ymf_ioctl: GETBLK w %d\n", val); - return put_user(val, p); - } - if (file->f_mode & FMODE_READ) { - if ((val = prog_dmabuf(state, 1))) - return val; - val = state->rpcm.dmabuf.fragsize; - YMFDBGX("ymf_ioctl: GETBLK r %d\n", val); - return put_user(val, p); - } - return -EINVAL; - - case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ - YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd); - return put_user(AFMT_S16_LE|AFMT_U8, p); - - case SNDCTL_DSP_SETFMT: /* Select sample format */ - if (get_user(val, p)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val); - if (val == AFMT_S16_LE || val == AFMT_U8) { - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.format = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf->ready = 0; - state->format.format = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - return put_user(state->format.format, p); - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, val); - if (val != 0) { - if (file->f_mode & FMODE_WRITE) { - ymf_wait_dac(state); - if (val == 1 || val == 2) { - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf = &state->wpcm.dmabuf; - dmabuf->ready = 0; - state->format.voices = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - if (file->f_mode & FMODE_READ) { - ymf_stop_adc(state); - if (val == 1 || val == 2) { - spin_lock_irqsave(&state->unit->reg_lock, flags); - dmabuf = &state->rpcm.dmabuf; - dmabuf->ready = 0; - state->format.voices = val; - ymf_pcm_update_shift(&state->format); - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - } - } - } - return put_user(state->format.voices, p); - - case SNDCTL_DSP_POST: - YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd); - /* - * Quoting OSS PG: - * The ioctl SNDCTL_DSP_POST is a lightweight version of - * SNDCTL_DSP_SYNC. It just tells to the driver that there - * is likely to be a pause in the output. This makes it - * possible for the device to handle the pause more - * intelligently. This ioctl doesn't block the application. - * - * The paragraph above is a clumsy way to say "flush ioctl". - * This ioctl is used by mpg123. - */ - spin_lock_irqsave(&state->unit->reg_lock, flags); - if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) { - ymf_start_dac(state); - } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - return 0; - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, p)) - return -EFAULT; - YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n", - cmd, - (val >> 16) & 0xFFFF, val & 0xFFFF, - (val >> 16) & 0xFFFF, val & 0xFFFF); - dmabuf = &state->wpcm.dmabuf; - dmabuf->ossfragshift = val & 0xffff; - dmabuf->ossmaxfrags = (val >> 16) & 0xffff; - if (dmabuf->ossfragshift < 4) - dmabuf->ossfragshift = 4; - if (dmabuf->ossfragshift > 15) - dmabuf->ossfragshift = 15; - return 0; - - case SNDCTL_DSP_GETOSPACE: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd); - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - dmabuf = &state->wpcm.dmabuf; - if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; - redzone = ymf_calc_lend(state->format.rate); - redzone <<= state->format.shift; - redzone *= 3; - spin_lock_irqsave(&state->unit->reg_lock, flags); - abinfo.fragsize = dmabuf->fragsize; - abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd); - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - dmabuf = &state->rpcm.dmabuf; - if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - return val; - spin_lock_irqsave(&state->unit->reg_lock, flags); - abinfo.fragsize = dmabuf->fragsize; - abinfo.bytes = dmabuf->count; - abinfo.fragstotal = dmabuf->numfrag; - abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd); - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETCAPS: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd); - /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, - p); */ - return put_user(0, p); - - case SNDCTL_DSP_GETIPTR: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd); - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - dmabuf = &state->rpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n", - cinfo.ptr, cinfo.bytes); - return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOPTR: - YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd); - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - dmabuf = &state->wpcm.dmabuf; - spin_lock_irqsave(&state->unit->reg_lock, flags); - cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count >> dmabuf->fragshift; - cinfo.ptr = dmabuf->hwptr; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); - YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n", - cinfo.ptr, cinfo.bytes); - return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_SETDUPLEX: - YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd); - return 0; /* Always duplex */ - - case SOUND_PCM_READ_RATE: - YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd); - return put_user(state->format.rate, p); - - case SOUND_PCM_READ_CHANNELS: - YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd); - return put_user(state->format.voices, p); - - case SOUND_PCM_READ_BITS: - YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd); - return put_user(AFMT_S16_LE, p); - - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_WRITE_FILTER: - case SOUND_PCM_READ_FILTER: - YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd); - return -ENOTTY; - - default: - /* - * Some programs mix up audio devices and ioctls - * or perhaps they expect "universal" ioctls, - * for instance we get SNDCTL_TMR_CONTINUE here. - * (mpg123 -g 100 ends here too - to be fixed.) - */ - YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd); - break; - } - return -ENOTTY; -} - -/* - * open(2) - * We use upper part of the minor to distinguish between soundcards. - * Channels are opened with a clone open. - */ -static int ymf_open(struct inode *inode, struct file *file) -{ - struct list_head *list; - ymfpci_t *unit = NULL; - int minor; - struct ymf_state *state; - int err; - - minor = iminor(inode); - if ((minor & 0x0F) == 3) { /* /dev/dspN */ - ; - } else { - return -ENXIO; - } - - unit = NULL; /* gcc warns */ - spin_lock(&ymf_devs_lock); - list_for_each(list, &ymf_devs) { - unit = list_entry(list, ymfpci_t, ymf_devs); - if (((unit->dev_audio ^ minor) & ~0x0F) == 0) - break; - } - spin_unlock(&ymf_devs_lock); - if (unit == NULL) - return -ENODEV; - - mutex_lock(&unit->open_mutex); - - if ((state = ymf_state_alloc(unit)) == NULL) { - mutex_unlock(&unit->open_mutex); - return -ENOMEM; - } - list_add_tail(&state->chain, &unit->states); - - file->private_data = state; - - /* - * ymf_read and ymf_write that we borrowed from cs46xx - * allocate buffers with prog_dmabuf(). We call prog_dmabuf - * here so that in case of DMA memory exhaustion open - * fails rather than write. - * - * XXX prog_dmabuf allocates voice. Should allocate explicitly, above. - */ - if (file->f_mode & FMODE_WRITE) { - if (!state->wpcm.dmabuf.ready) { - if ((err = prog_dmabuf(state, 0)) != 0) { - goto out_nodma; - } - } - } - if (file->f_mode & FMODE_READ) { - if (!state->rpcm.dmabuf.ready) { - if ((err = prog_dmabuf(state, 1)) != 0) { - goto out_nodma; - } - } - } - -#if 0 /* test if interrupts work */ - ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ - ymfpci_writeb(unit, YDSXGR_TIMERCTRL, - (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN)); -#endif - mutex_unlock(&unit->open_mutex); - - return nonseekable_open(inode, file); - -out_nodma: - /* - * XXX Broken custom: "goto out_xxx" in other place is - * a nestable exception, but here it is not nestable due to semaphore. - * XXX Doubtful technique of self-describing objects.... - */ - dealloc_dmabuf(unit, &state->wpcm.dmabuf); - dealloc_dmabuf(unit, &state->rpcm.dmabuf); - ymf_pcm_free_substream(&state->wpcm); - ymf_pcm_free_substream(&state->rpcm); - - list_del(&state->chain); - kfree(state); - - mutex_unlock(&unit->open_mutex); - return err; -} - -static int ymf_release(struct inode *inode, struct file *file) -{ - struct ymf_state *state = (struct ymf_state *)file->private_data; - ymfpci_t *unit = state->unit; - -#if 0 /* test if interrupts work */ - ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0); -#endif - - mutex_lock(&unit->open_mutex); - - /* - * XXX Solve the case of O_NONBLOCK close - don't deallocate here. - * Deallocate when unloading the driver and we can wait. - */ - ymf_wait_dac(state); - ymf_stop_adc(state); /* fortunately, it's immediate */ - dealloc_dmabuf(unit, &state->wpcm.dmabuf); - dealloc_dmabuf(unit, &state->rpcm.dmabuf); - ymf_pcm_free_substream(&state->wpcm); - ymf_pcm_free_substream(&state->rpcm); - - list_del(&state->chain); - file->private_data = NULL; /* Can you tell I programmed Solaris */ - kfree(state); - - mutex_unlock(&unit->open_mutex); - - return 0; -} - -/* - * Mixer operations are based on cs46xx. - */ -static int ymf_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct list_head *list; - ymfpci_t *unit; - int i; - - spin_lock(&ymf_devs_lock); - list_for_each(list, &ymf_devs) { - unit = list_entry(list, ymfpci_t, ymf_devs); - for (i = 0; i < NR_AC97; i++) { - if (unit->ac97_codec[i] != NULL && - unit->ac97_codec[i]->dev_mixer == minor) { - spin_unlock(&ymf_devs_lock); - goto match; - } - } - } - spin_unlock(&ymf_devs_lock); - return -ENODEV; - - match: - file->private_data = unit->ac97_codec[i]; - - return nonseekable_open(inode, file); -} - -static int ymf_ioctl_mixdev(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ac97_codec *codec = (struct ac97_codec *)file->private_data; - - return codec->mixer_ioctl(codec, cmd, arg); -} - -static int ymf_release_mixdev(struct inode *inode, struct file *file) -{ - return 0; -} - -static /*const*/ struct file_operations ymf_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = ymf_read, - .write = ymf_write, - .poll = ymf_poll, - .ioctl = ymf_ioctl, - .mmap = ymf_mmap, - .open = ymf_open, - .release = ymf_release, -}; - -static /*const*/ struct file_operations ymf_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = ymf_ioctl_mixdev, - .open = ymf_open_mixdev, - .release = ymf_release_mixdev, -}; - -/* - */ - -static int ymf_suspend(struct pci_dev *pcidev, pm_message_t unused) -{ - struct ymf_unit *unit = pci_get_drvdata(pcidev); - unsigned long flags; - struct ymf_dmabuf *dmabuf; - struct list_head *p; - struct ymf_state *state; - struct ac97_codec *codec; - int i; - - spin_lock_irqsave(&unit->reg_lock, flags); - - unit->suspended = 1; - - for (i = 0; i < NR_AC97; i++) { - if ((codec = unit->ac97_codec[i]) != NULL) - ac97_save_state(codec); - } - - list_for_each(p, &unit->states) { - state = list_entry(p, struct ymf_state, chain); - - dmabuf = &state->wpcm.dmabuf; - dmabuf->hwptr = dmabuf->swptr = 0; - dmabuf->total_bytes = 0; - dmabuf->count = 0; - - dmabuf = &state->rpcm.dmabuf; - dmabuf->hwptr = dmabuf->swptr = 0; - dmabuf->total_bytes = 0; - dmabuf->count = 0; - } - - ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); - ymfpci_disable_dsp(unit); - - spin_unlock_irqrestore(&unit->reg_lock, flags); - - return 0; -} - -static int ymf_resume(struct pci_dev *pcidev) -{ - struct ymf_unit *unit = pci_get_drvdata(pcidev); - unsigned long flags; - struct list_head *p; - struct ymf_state *state; - struct ac97_codec *codec; - int i; - - ymfpci_aclink_reset(unit->pci); - ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */ - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - /* XXX At this time the legacy registers are probably deprogrammed. */ -#endif - - ymfpci_download_image(unit); - - ymf_memload(unit); - - spin_lock_irqsave(&unit->reg_lock, flags); - - if (unit->start_count) { - ymfpci_writel(unit, YDSXGR_MODE, 3); - unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; - } - - for (i = 0; i < NR_AC97; i++) { - if ((codec = unit->ac97_codec[i]) != NULL) - ac97_restore_state(codec); - } - - unit->suspended = 0; - list_for_each(p, &unit->states) { - state = list_entry(p, struct ymf_state, chain); - wake_up(&state->wpcm.dmabuf.wait); - wake_up(&state->rpcm.dmabuf.wait); - } - - spin_unlock_irqrestore(&unit->reg_lock, flags); - return 0; -} - -/* - * initialization routines - */ - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - -static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev) -{ - int v; - int mpuio = -1, oplio = -1; - - switch (unit->iomidi) { - case 0x330: - mpuio = 0; - break; - case 0x300: - mpuio = 1; - break; - case 0x332: - mpuio = 2; - break; - case 0x334: - mpuio = 3; - break; - default: ; - } - - switch (unit->iosynth) { - case 0x388: - oplio = 0; - break; - case 0x398: - oplio = 1; - break; - case 0x3a0: - oplio = 2; - break; - case 0x3a8: - oplio = 3; - break; - default: ; - } - - if (mpuio >= 0 || oplio >= 0) { - /* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */ - v = 0x001e; - pci_write_config_word(pcidev, PCIR_LEGCTRL, v); - - switch (pcidev->device) { - case PCI_DEVICE_ID_YAMAHA_724: - case PCI_DEVICE_ID_YAMAHA_740: - case PCI_DEVICE_ID_YAMAHA_724F: - case PCI_DEVICE_ID_YAMAHA_740C: - v = 0x8800; - if (mpuio >= 0) { v |= mpuio<<4; } - if (oplio >= 0) { v |= oplio; } - pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); - break; - - case PCI_DEVICE_ID_YAMAHA_744: - case PCI_DEVICE_ID_YAMAHA_754: - v = 0x8800; - pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); - if (oplio >= 0) { - pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth); - } - if (mpuio >= 0) { - pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi); - } - break; - - default: - printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n", - pcidev->device); - return -EINVAL; - } - } - - return 0; -} -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - -static void ymfpci_aclink_reset(struct pci_dev * pci) -{ - u8 cmd; - - /* - * In the 744, 754 only 0x01 exists, 0x02 is undefined. - * It does not seem to hurt to trip both regardless of revision. - */ - pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); - pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); - pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03); - pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); - - pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0); - pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0); -} - -static void ymfpci_enable_dsp(ymfpci_t *codec) -{ - ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001); -} - -static void ymfpci_disable_dsp(ymfpci_t *codec) -{ - u32 val; - int timeout = 1000; - - val = ymfpci_readl(codec, YDSXGR_CONFIG); - if (val) - ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000); - while (timeout-- > 0) { - val = ymfpci_readl(codec, YDSXGR_STATUS); - if ((val & 0x00000002) == 0) - break; - } -} - -#include "ymfpci_image.h" - -static void ymfpci_download_image(ymfpci_t *codec) -{ - int i, ver_1e; - u16 ctrl; - - ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000); - ymfpci_disable_dsp(codec); - ymfpci_writel(codec, YDSXGR_MODE, 0x00010000); - ymfpci_writel(codec, YDSXGR_MODE, 0x00000000); - ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000); - ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000); - ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000); - ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000); - ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000); - ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); - ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); - - /* setup DSP instruction code */ - for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) - ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); - - switch (codec->pci->device) { - case PCI_DEVICE_ID_YAMAHA_724F: - case PCI_DEVICE_ID_YAMAHA_740C: - case PCI_DEVICE_ID_YAMAHA_744: - case PCI_DEVICE_ID_YAMAHA_754: - ver_1e = 1; - break; - default: - ver_1e = 0; - } - - if (ver_1e) { - /* setup control instruction code */ - for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) - ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]); - } else { - for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) - ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]); - } - - ymfpci_enable_dsp(codec); - - /* 0.02s sounds not too bad, we may do schedule_timeout() later. */ - mdelay(20); /* seems we need some delay after downloading image.. */ -} - -static int ymfpci_memalloc(ymfpci_t *codec) -{ - unsigned int playback_ctrl_size; - unsigned int bank_size_playback; - unsigned int bank_size_capture; - unsigned int bank_size_effect; - unsigned int size; - unsigned int off; - char *ptr; - dma_addr_t pba; - int voice, bank; - - playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; - bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2; - bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2; - bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2; - codec->work_size = YDSXG_DEFAULT_WORK_SIZE; - - size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + - ((bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) + - ((bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) + - ((bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) + - codec->work_size; - - ptr = pci_alloc_consistent(codec->pci, size + 0xff, &pba); - if (ptr == NULL) - return -ENOMEM; - codec->dma_area_va = ptr; - codec->dma_area_ba = pba; - codec->dma_area_size = size + 0xff; - - off = (unsigned long)ptr & 0xff; - if (off) { - ptr += 0x100 - off; - pba += 0x100 - off; - } - - /* - * Hardware requires only ptr[playback_ctrl_size] zeroed, - * but in our judgement it is a wrong kind of savings, so clear it all. - */ - memset(ptr, 0, size); - - codec->ctrl_playback = (u32 *)ptr; - codec->ctrl_playback_ba = pba; - codec->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); - ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; - pba += (playback_ctrl_size + 0x00ff) & ~0x00ff; - - off = 0; - for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { - codec->voices[voice].number = voice; - codec->voices[voice].bank = - (ymfpci_playback_bank_t *) (ptr + off); - codec->voices[voice].bank_ba = pba + off; - off += 2 * bank_size_playback; /* 2 banks */ - } - off = (off + 0xff) & ~0xff; - ptr += off; - pba += off; - - off = 0; - codec->bank_base_capture = pba; - for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) - for (bank = 0; bank < 2; bank++) { - codec->bank_capture[voice][bank] = - (ymfpci_capture_bank_t *) (ptr + off); - off += bank_size_capture; - } - off = (off + 0xff) & ~0xff; - ptr += off; - pba += off; - - off = 0; - codec->bank_base_effect = pba; - for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) - for (bank = 0; bank < 2; bank++) { - codec->bank_effect[voice][bank] = - (ymfpci_effect_bank_t *) (ptr + off); - off += bank_size_effect; - } - off = (off + 0xff) & ~0xff; - ptr += off; - pba += off; - - codec->work_base = pba; - - return 0; -} - -static void ymfpci_memfree(ymfpci_t *codec) -{ - ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0); - ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0); - ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0); - ymfpci_writel(codec, YDSXGR_WORKBASE, 0); - ymfpci_writel(codec, YDSXGR_WORKSIZE, 0); - pci_free_consistent(codec->pci, - codec->dma_area_size, codec->dma_area_va, codec->dma_area_ba); -} - -static void ymf_memload(ymfpci_t *unit) -{ - - ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, unit->ctrl_playback_ba); - ymfpci_writel(unit, YDSXGR_RECCTRLBASE, unit->bank_base_capture); - ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, unit->bank_base_effect); - ymfpci_writel(unit, YDSXGR_WORKBASE, unit->work_base); - ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2); - - /* S/PDIF output initialization */ - ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0); - ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS, - SND_PCM_AES0_CON_EMPHASIS_NONE | - (SND_PCM_AES1_CON_ORIGINAL << 8) | - (SND_PCM_AES1_CON_PCM_CODER << 8)); - - /* S/PDIF input initialization */ - ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0); - - /* move this volume setup to mixer */ - ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); - ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0); - ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); - ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); -} - -static int ymf_ac97_init(ymfpci_t *unit, int num_ac97) -{ - struct ac97_codec *codec; - u16 eid; - - if ((codec = ac97_alloc_codec()) == NULL) - return -ENOMEM; - - /* initialize some basic codec information, other fields will be filled - in ac97_probe_codec */ - codec->private_data = unit; - codec->id = num_ac97; - - codec->codec_read = ymfpci_codec_read; - codec->codec_write = ymfpci_codec_write; - - if (ac97_probe_codec(codec) == 0) { - printk(KERN_ERR "ymfpci: ac97_probe_codec failed\n"); - goto out_kfree; - } - - eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); - if (eid==0xFFFF) { - printk(KERN_WARNING "ymfpci: no codec attached ?\n"); - goto out_kfree; - } - - unit->ac97_features = eid; - - if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) { - printk(KERN_ERR "ymfpci: couldn't register mixer!\n"); - goto out_kfree; - } - - unit->ac97_codec[num_ac97] = codec; - - return 0; - out_kfree: - ac97_release_codec(codec); - return -ENODEV; -} - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY -# ifdef MODULE -static int mpu_io; -static int synth_io; -module_param(mpu_io, int, 0); -module_param(synth_io, int, 0); -# else -static int mpu_io = 0x330; -static int synth_io = 0x388; -# endif -static int assigned; -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - -static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent) -{ - u16 ctrl; - unsigned long base; - ymfpci_t *codec; - - int err; - - if ((err = pci_enable_device(pcidev)) != 0) { - printk(KERN_ERR "ymfpci: pci_enable_device failed\n"); - return err; - } - base = pci_resource_start(pcidev, 0); - - if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "ymfpci: no core\n"); - return -ENOMEM; - } - memset(codec, 0, sizeof(*codec)); - - spin_lock_init(&codec->reg_lock); - spin_lock_init(&codec->voice_lock); - spin_lock_init(&codec->ac97_lock); - mutex_init(&codec->open_mutex); - INIT_LIST_HEAD(&codec->states); - codec->pci = pcidev; - - pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); - - if (request_mem_region(base, 0x8000, "ymfpci") == NULL) { - printk(KERN_ERR "ymfpci: unable to request mem region\n"); - goto out_free; - } - - if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) { - printk(KERN_ERR "ymfpci: unable to map registers\n"); - goto out_release_region; - } - - pci_set_master(pcidev); - - printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", - (char *)ent->driver_data, base, pcidev->irq); - - ymfpci_aclink_reset(pcidev); - if (ymfpci_codec_ready(codec, 0, 1) < 0) - goto out_unmap; - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - if (assigned == 0) { - codec->iomidi = mpu_io; - codec->iosynth = synth_io; - if (ymfpci_setup_legacy(codec, pcidev) < 0) - goto out_unmap; - assigned = 1; - } -#endif - - ymfpci_download_image(codec); - - if (ymfpci_memalloc(codec) < 0) - goto out_disable_dsp; - ymf_memload(codec); - - if (request_irq(pcidev->irq, ymf_interrupt, IRQF_SHARED, "ymfpci", codec) != 0) { - printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", - pcidev->irq); - goto out_memfree; - } - - /* register /dev/dsp */ - if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) { - printk(KERN_ERR "ymfpci: unable to register dsp\n"); - goto out_free_irq; - } - - /* - * Poke just the primary for the moment. - */ - if ((err = ymf_ac97_init(codec, 0)) != 0) - goto out_unregister_sound_dsp; - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - codec->opl3_data.name = "ymfpci"; - codec->mpu_data.name = "ymfpci"; - - codec->opl3_data.io_base = codec->iosynth; - codec->opl3_data.irq = -1; - - codec->mpu_data.io_base = codec->iomidi; - codec->mpu_data.irq = -1; /* May be different from our PCI IRQ. */ - - if (codec->iomidi) { - if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) { - codec->iomidi = 0; /* XXX kludge */ - } - } -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ - - /* put it into driver list */ - spin_lock(&ymf_devs_lock); - list_add_tail(&codec->ymf_devs, &ymf_devs); - spin_unlock(&ymf_devs_lock); - pci_set_drvdata(pcidev, codec); - - return 0; - - out_unregister_sound_dsp: - unregister_sound_dsp(codec->dev_audio); - out_free_irq: - free_irq(pcidev->irq, codec); - out_memfree: - ymfpci_memfree(codec); - out_disable_dsp: - ymfpci_disable_dsp(codec); - ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); - ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); - ymfpci_writel(codec, YDSXGR_STATUS, ~0); - out_unmap: - iounmap(codec->reg_area_virt); - out_release_region: - release_mem_region(pci_resource_start(pcidev, 0), 0x8000); - out_free: - if (codec->ac97_codec[0]) - ac97_release_codec(codec->ac97_codec[0]); - return -ENODEV; -} - -static void __devexit ymf_remove_one(struct pci_dev *pcidev) -{ - __u16 ctrl; - ymfpci_t *codec = pci_get_drvdata(pcidev); - - /* remove from list of devices */ - spin_lock(&ymf_devs_lock); - list_del(&codec->ymf_devs); - spin_unlock(&ymf_devs_lock); - - unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); - ac97_release_codec(codec->ac97_codec[0]); - unregister_sound_dsp(codec->dev_audio); - free_irq(pcidev->irq, codec); - ymfpci_memfree(codec); - ymfpci_writel(codec, YDSXGR_STATUS, ~0); - ymfpci_disable_dsp(codec); - ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); - ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); - iounmap(codec->reg_area_virt); - release_mem_region(pci_resource_start(pcidev, 0), 0x8000); -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - if (codec->iomidi) { - unload_uart401(&codec->mpu_data); - } -#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ -} - -MODULE_AUTHOR("Jaroslav Kysela"); -MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio"); -MODULE_LICENSE("GPL"); - -static struct pci_driver ymfpci_driver = { - .name = "ymfpci", - .id_table = ymf_id_tbl, - .probe = ymf_probe_one, - .remove = __devexit_p(ymf_remove_one), - .suspend = ymf_suspend, - .resume = ymf_resume -}; - -static int __init ymf_init_module(void) -{ - return pci_register_driver(&ymfpci_driver); -} - -static void __exit ymf_cleanup_module (void) -{ - pci_unregister_driver(&ymfpci_driver); -} - -module_init(ymf_init_module); -module_exit(ymf_cleanup_module); diff --git a/sound/oss/ymfpci.h b/sound/oss/ymfpci.h deleted file mode 100644 index ac1785f2b7..0000000000 --- a/sound/oss/ymfpci.h +++ /dev/null @@ -1,361 +0,0 @@ -#ifndef __YMFPCI_H -#define __YMFPCI_H - -/* - * Copyright (c) by Jaroslav Kysela - * Definitions for Yahama YMF724/740/744/754 chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -#include -#include - -/* - * Direct registers - */ - -/* #define YMFREG(codec, reg) (codec->port + YDSXGR_##reg) */ - -#define YDSXGR_INTFLAG 0x0004 -#define YDSXGR_ACTIVITY 0x0006 -#define YDSXGR_GLOBALCTRL 0x0008 -#define YDSXGR_ZVCTRL 0x000A -#define YDSXGR_TIMERCTRL 0x0010 -#define YDSXGR_TIMERCTRL_TEN 0x0001 -#define YDSXGR_TIMERCTRL_TIEN 0x0002 -#define YDSXGR_TIMERCOUNT 0x0012 -#define YDSXGR_SPDIFOUTCTRL 0x0018 -#define YDSXGR_SPDIFOUTSTATUS 0x001C -#define YDSXGR_EEPROMCTRL 0x0020 -#define YDSXGR_SPDIFINCTRL 0x0034 -#define YDSXGR_SPDIFINSTATUS 0x0038 -#define YDSXGR_DSPPROGRAMDL 0x0048 -#define YDSXGR_DLCNTRL 0x004C -#define YDSXGR_GPIOININTFLAG 0x0050 -#define YDSXGR_GPIOININTENABLE 0x0052 -#define YDSXGR_GPIOINSTATUS 0x0054 -#define YDSXGR_GPIOOUTCTRL 0x0056 -#define YDSXGR_GPIOFUNCENABLE 0x0058 -#define YDSXGR_GPIOTYPECONFIG 0x005A -#define YDSXGR_AC97CMDDATA 0x0060 -#define YDSXGR_AC97CMDADR 0x0062 -#define YDSXGR_PRISTATUSDATA 0x0064 -#define YDSXGR_PRISTATUSADR 0x0066 -#define YDSXGR_SECSTATUSDATA 0x0068 -#define YDSXGR_SECSTATUSADR 0x006A -#define YDSXGR_SECCONFIG 0x0070 -#define YDSXGR_LEGACYOUTVOL 0x0080 -#define YDSXGR_LEGACYOUTVOLL 0x0080 -#define YDSXGR_LEGACYOUTVOLR 0x0082 -#define YDSXGR_NATIVEDACOUTVOL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLR 0x0086 -#define YDSXGR_SPDIFOUTVOL 0x0088 -#define YDSXGR_SPDIFOUTVOLL 0x0088 -#define YDSXGR_SPDIFOUTVOLR 0x008A -#define YDSXGR_AC3OUTVOL 0x008C -#define YDSXGR_AC3OUTVOLL 0x008C -#define YDSXGR_AC3OUTVOLR 0x008E -#define YDSXGR_PRIADCOUTVOL 0x0090 -#define YDSXGR_PRIADCOUTVOLL 0x0090 -#define YDSXGR_PRIADCOUTVOLR 0x0092 -#define YDSXGR_LEGACYLOOPVOL 0x0094 -#define YDSXGR_LEGACYLOOPVOLL 0x0094 -#define YDSXGR_LEGACYLOOPVOLR 0x0096 -#define YDSXGR_NATIVEDACLOOPVOL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLR 0x009A -#define YDSXGR_SPDIFLOOPVOL 0x009C -#define YDSXGR_SPDIFLOOPVOLL 0x009E -#define YDSXGR_SPDIFLOOPVOLR 0x009E -#define YDSXGR_AC3LOOPVOL 0x00A0 -#define YDSXGR_AC3LOOPVOLL 0x00A0 -#define YDSXGR_AC3LOOPVOLR 0x00A2 -#define YDSXGR_PRIADCLOOPVOL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLR 0x00A6 -#define YDSXGR_NATIVEADCINVOL 0x00A8 -#define YDSXGR_NATIVEADCINVOLL 0x00A8 -#define YDSXGR_NATIVEADCINVOLR 0x00AA -#define YDSXGR_NATIVEDACINVOL 0x00AC -#define YDSXGR_NATIVEDACINVOLL 0x00AC -#define YDSXGR_NATIVEDACINVOLR 0x00AE -#define YDSXGR_BUF441OUTVOL 0x00B0 -#define YDSXGR_BUF441OUTVOLL 0x00B0 -#define YDSXGR_BUF441OUTVOLR 0x00B2 -#define YDSXGR_BUF441LOOPVOL 0x00B4 -#define YDSXGR_BUF441LOOPVOLL 0x00B4 -#define YDSXGR_BUF441LOOPVOLR 0x00B6 -#define YDSXGR_SPDIFOUTVOL2 0x00B8 -#define YDSXGR_SPDIFOUTVOL2L 0x00B8 -#define YDSXGR_SPDIFOUTVOL2R 0x00BA -#define YDSXGR_SPDIFLOOPVOL2 0x00BC -#define YDSXGR_SPDIFLOOPVOL2L 0x00BC -#define YDSXGR_SPDIFLOOPVOL2R 0x00BE -#define YDSXGR_ADCSLOTSR 0x00C0 -#define YDSXGR_RECSLOTSR 0x00C4 -#define YDSXGR_ADCFORMAT 0x00C8 -#define YDSXGR_RECFORMAT 0x00CC -#define YDSXGR_P44SLOTSR 0x00D0 -#define YDSXGR_STATUS 0x0100 -#define YDSXGR_CTRLSELECT 0x0104 -#define YDSXGR_MODE 0x0108 -#define YDSXGR_SAMPLECOUNT 0x010C -#define YDSXGR_NUMOFSAMPLES 0x0110 -#define YDSXGR_CONFIG 0x0114 -#define YDSXGR_PLAYCTRLSIZE 0x0140 -#define YDSXGR_RECCTRLSIZE 0x0144 -#define YDSXGR_EFFCTRLSIZE 0x0148 -#define YDSXGR_WORKSIZE 0x014C -#define YDSXGR_MAPOFREC 0x0150 -#define YDSXGR_MAPOFEFFECT 0x0154 -#define YDSXGR_PLAYCTRLBASE 0x0158 -#define YDSXGR_RECCTRLBASE 0x015C -#define YDSXGR_EFFCTRLBASE 0x0160 -#define YDSXGR_WORKBASE 0x0164 -#define YDSXGR_DSPINSTRAM 0x1000 -#define YDSXGR_CTRLINSTRAM 0x4000 - -#define YDSXG_AC97READCMD 0x8000 -#define YDSXG_AC97WRITECMD 0x0000 - -#define PCIR_LEGCTRL 0x40 -#define PCIR_ELEGCTRL 0x42 -#define PCIR_DSXGCTRL 0x48 -#define PCIR_DSXPWRCTRL1 0x4a -#define PCIR_DSXPWRCTRL2 0x4e -#define PCIR_OPLADR 0x60 -#define PCIR_SBADR 0x62 -#define PCIR_MPUADR 0x64 - -#define YDSXG_DSPLENGTH 0x0080 -#define YDSXG_CTRLLENGTH 0x3000 - -#define YDSXG_DEFAULT_WORK_SIZE 0x0400 - -#define YDSXG_PLAYBACK_VOICES 64 -#define YDSXG_CAPTURE_VOICES 2 -#define YDSXG_EFFECT_VOICES 5 - -/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define NR_AC97 2 - -#define YMF_SAMPF 256 /* Samples per frame @48000 */ - -/* - * The slot/voice control bank (2 of these per voice) - */ - -typedef struct stru_ymfpci_playback_bank { - u32 format; - u32 loop_default; - u32 base; /* 32-bit address */ - u32 loop_start; /* 32-bit offset */ - u32 loop_end; /* 32-bit offset */ - u32 loop_frac; /* 8-bit fraction - loop_start */ - u32 delta_end; /* pitch delta end */ - u32 lpfK_end; - u32 eg_gain_end; - u32 left_gain_end; - u32 right_gain_end; - u32 eff1_gain_end; - u32 eff2_gain_end; - u32 eff3_gain_end; - u32 lpfQ; - u32 status; /* P3: Always 0 for some reason. */ - u32 num_of_frames; - u32 loop_count; - u32 start; /* P3: J. reads this to know where chip is. */ - u32 start_frac; - u32 delta; - u32 lpfK; - u32 eg_gain; - u32 left_gain; - u32 right_gain; - u32 eff1_gain; - u32 eff2_gain; - u32 eff3_gain; - u32 lpfD1; - u32 lpfD2; -} ymfpci_playback_bank_t; - -typedef struct stru_ymfpci_capture_bank { - u32 base; /* 32-bit address (aligned at 4) */ - u32 loop_end; /* size in BYTES (aligned at 4) */ - u32 start; /* 32-bit offset */ - u32 num_of_loops; /* counter */ -} ymfpci_capture_bank_t; - -typedef struct stru_ymfpci_effect_bank { - u32 base; /* 32-bit address */ - u32 loop_end; /* 32-bit offset */ - u32 start; /* 32-bit offset */ - u32 temp; -} ymfpci_effect_bank_t; - -typedef struct ymf_voice ymfpci_voice_t; -/* - * Throughout the code Yaroslav names YMF unit pointer "codec" - * even though it does not correspond to any codec. Must be historic. - * We replace it with "unit" over time. - * AC97 parts use "codec" to denote a codec, naturally. - */ -typedef struct ymf_unit ymfpci_t; - -typedef enum { - YMFPCI_PCM, - YMFPCI_SYNTH, - YMFPCI_MIDI -} ymfpci_voice_type_t; - -struct ymf_voice { - // ymfpci_t *codec; - int number; - char use, pcm, synth, midi; // bool - ymfpci_playback_bank_t *bank; - struct ymf_pcm *ypcm; - dma_addr_t bank_ba; -}; - -struct ymf_capture { - // struct ymf_unit *unit; - int use; - ymfpci_capture_bank_t *bank; - struct ymf_pcm *ypcm; -}; - -struct ymf_unit { - u8 rev; /* PCI revision */ - void __iomem *reg_area_virt; - void *dma_area_va; - dma_addr_t dma_area_ba; - unsigned int dma_area_size; - - dma_addr_t bank_base_capture; - dma_addr_t bank_base_effect; - dma_addr_t work_base; - unsigned int work_size; - - u32 *ctrl_playback; - dma_addr_t ctrl_playback_ba; - ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; - ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; - ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; - - int start_count; - int suspended; - - u32 active_bank; - struct ymf_voice voices[YDSXG_PLAYBACK_VOICES]; - struct ymf_capture capture[YDSXG_CAPTURE_VOICES]; - - struct ac97_codec *ac97_codec[NR_AC97]; - u16 ac97_features; - - struct pci_dev *pci; - -#ifdef CONFIG_SOUND_YMFPCI_LEGACY - /* legacy hardware resources */ - unsigned int iosynth, iomidi; - struct address_info opl3_data, mpu_data; -#endif - - spinlock_t reg_lock; - spinlock_t voice_lock; - spinlock_t ac97_lock; - - /* soundcore stuff */ - int dev_audio; - struct mutex open_mutex; - - struct list_head ymf_devs; - struct list_head states; /* List of states for this unit */ -}; - -struct ymf_dmabuf { - dma_addr_t dma_addr; - void *rawbuf; - unsigned buforder; - - /* OSS buffer management stuff */ - unsigned numfrag; - unsigned fragshift; - - /* our buffer acts like a circular ring */ - unsigned hwptr; /* where dma last started */ - unsigned swptr; /* where driver last clear/filled */ - int count; /* fill count */ - unsigned total_bytes; /* total bytes dmaed by hardware */ - - wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ - - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; /* Total rawbuf[] size */ - - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; -}; - -struct ymf_pcm_format { - int format; /* OSS format */ - int rate; /* rate in Hz */ - int voices; /* number of voices */ - int shift; /* redundant, computed from the above */ -}; - -typedef enum { - PLAYBACK_VOICE, - CAPTURE_REC, - CAPTURE_AC97, - EFFECT_DRY_LEFT, - EFFECT_DRY_RIGHT, - EFFECT_EFF1, - EFFECT_EFF2, - EFFECT_EFF3 -} ymfpci_pcm_type_t; - -/* This is variant record, but we hate unions. Little waste on pointers []. */ -struct ymf_pcm { - ymfpci_pcm_type_t type; - struct ymf_state *state; - - ymfpci_voice_t *voices[2]; - int capture_bank_number; - - struct ymf_dmabuf dmabuf; - int running; - int spdif; -}; - -/* - * "Software" or virtual channel, an instance of opened /dev/dsp. - * It may have two physical channels (pcms) for duplex operations. - */ - -struct ymf_state { - struct list_head chain; - struct ymf_unit *unit; /* backpointer */ - struct ymf_pcm rpcm, wpcm; - struct ymf_pcm_format format; -}; - -#endif /* __YMFPCI_H */ diff --git a/sound/oss/ymfpci_image.h b/sound/oss/ymfpci_image.h deleted file mode 100644 index 112f2fff6c..0000000000 --- a/sound/oss/ymfpci_image.h +++ /dev/null @@ -1,1565 +0,0 @@ -#ifndef _HWMCODE_ -#define _HWMCODE_ - -static u32 DspInst[YDSXG_DSPLENGTH / 4] = { - 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, - 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, - 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, - 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - -static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x001A82, 0x032D0D, 0x000810, 0x10043A, - 0x02D38D, 0x000810, 0x18043A, 0x00010D, - 0x020015, 0x0000FD, 0x000020, 0x038860, - 0x039060, 0x038060, 0x038040, 0x038040, - 0x038040, 0x018040, 0x000A7D, 0x038040, - 0x038040, 0x018040, 0x200402, 0x000882, - 0x08001A, 0x000904, 0x015986, 0x000007, - 0x260007, 0x000007, 0x000007, 0x018A06, - 0x000007, 0x030C8D, 0x000810, 0x18043A, - 0x260007, 0x00087D, 0x018042, 0x00160A, - 0x04A206, 0x000007, 0x00218D, 0x000810, - 0x08043A, 0x21C206, 0x000007, 0x0007FD, - 0x018042, 0x08000A, 0x000904, 0x029386, - 0x000195, 0x090D04, 0x000007, 0x000820, - 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, - 0x032206, 0x018040, 0x000A7D, 0x038042, - 0x13804A, 0x18000A, 0x001820, 0x059060, - 0x058860, 0x018040, 0x0000FD, 0x018042, - 0x70000A, 0x000115, 0x071144, 0x032386, - 0x030000, 0x007020, 0x034A06, 0x018040, - 0x00348D, 0x000810, 0x08043A, 0x21EA06, - 0x000007, 0x02D38D, 0x000810, 0x18043A, - 0x018206, 0x000007, 0x240007, 0x000F8D, - 0x000810, 0x00163A, 0x002402, 0x005C02, - 0x0028FD, 0x000020, 0x018040, 0x08000D, - 0x000815, 0x510984, 0x000007, 0x00004D, - 0x000E5D, 0x000E02, 0x00418D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00008D, - 0x000924, 0x000F02, 0x00458D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x018386, 0x000007, 0x01AA06, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x218086, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x055A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x034986, 0x000007, 0x002104, 0x034986, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x06C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00688D, 0x000810, 0x08043A, 0x288A06, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x060206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x215886, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x212086, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x07DA86, 0x00057D, 0x002820, - 0x03B060, 0x07F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x07FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x008D8D, 0x000810, - 0x08043A, 0x288A06, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x095186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x007FBD, 0x383DC4, - 0x000007, 0x001A7D, 0x001375, 0x018042, - 0x09004A, 0x10000A, 0x0B8D04, 0x139504, - 0x000007, 0x000820, 0x019060, 0x001104, - 0x212086, 0x010040, 0x0017FD, 0x018042, - 0x08000A, 0x000904, 0x212286, 0x000007, - 0x00197D, 0x038042, 0x09804A, 0x10000A, - 0x000924, 0x001664, 0x0011FD, 0x038042, - 0x2B804A, 0x19804A, 0x00008D, 0x218944, - 0x000007, 0x002244, 0x0AE186, 0x000007, - 0x001A64, 0x002A24, 0x00197D, 0x080102, - 0x100122, 0x000820, 0x039060, 0x018040, - 0x003DFD, 0x00008D, 0x000820, 0x018040, - 0x001375, 0x001A7D, 0x010042, 0x09804A, - 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, - 0x309144, 0x000007, 0x00060D, 0x000A15, - 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, - 0x000464, 0x01B3E4, 0x0232E4, 0x000464, - 0x000464, 0x000464, 0x000464, 0x00040D, - 0x08B1C4, 0x000007, 0x000820, 0x000BF5, - 0x030040, 0x00197D, 0x038042, 0x09804A, - 0x000A24, 0x08000A, 0x080E64, 0x000007, - 0x100122, 0x000820, 0x031060, 0x010040, - 0x0064AC, 0x00027D, 0x000020, 0x018040, - 0x00107D, 0x018042, 0x0011FD, 0x3B804A, - 0x09804A, 0x20000A, 0x000095, 0x1A1144, - 0x00A144, 0x0D2086, 0x00040D, 0x00B984, - 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, - 0x09804A, 0x28000A, 0x000095, 0x010924, - 0x002A64, 0x0D1186, 0x000007, 0x002904, - 0x0D2286, 0x000007, 0x0D2A06, 0x080002, - 0x00008D, 0x00387D, 0x000820, 0x018040, - 0x00127D, 0x018042, 0x10000A, 0x003904, - 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, - 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, - 0x000015, 0x00082D, 0x02C78D, 0x000820, - 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, - 0x0E7186, 0x400025, 0x00008D, 0x110944, - 0x000007, 0x00018D, 0x109504, 0x000007, - 0x009164, 0x000424, 0x000424, 0x000424, - 0x100102, 0x280002, 0x02C68D, 0x000820, - 0x0EC206, 0x00018D, 0x00042D, 0x00008D, - 0x109504, 0x000007, 0x00020D, 0x109184, - 0x000007, 0x02C70D, 0x000820, 0x00008D, - 0x0038FD, 0x018040, 0x003BFD, 0x001020, - 0x03A860, 0x000815, 0x313184, 0x212184, - 0x000007, 0x03B060, 0x03A060, 0x018040, - 0x0022FD, 0x000095, 0x010924, 0x000424, - 0x000424, 0x001264, 0x100102, 0x000820, - 0x039060, 0x018040, 0x001924, 0x00FB8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x000424, - 0x000424, 0x00117D, 0x018042, 0x08000A, - 0x000A24, 0x280502, 0x280C02, 0x09800D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x0022FD, 0x018042, 0x08000A, 0x000095, - 0x280DC4, 0x011924, 0x00197D, 0x018042, - 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, - 0x113144, 0x0A8D04, 0x000007, 0x080A44, - 0x129504, 0x000007, 0x0023FD, 0x001020, - 0x038040, 0x101244, 0x000007, 0x000820, - 0x039060, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x10FA86, 0x000007, - 0x003BFD, 0x000100, 0x000A10, 0x0B807A, - 0x13804A, 0x090984, 0x000007, 0x000095, - 0x013D04, 0x118086, 0x10000A, 0x100002, - 0x090984, 0x000007, 0x038042, 0x11804A, - 0x090D04, 0x000007, 0x10000A, 0x090D84, - 0x000007, 0x00257D, 0x000820, 0x018040, - 0x00010D, 0x000810, 0x28143A, 0x00127D, - 0x018042, 0x20000A, 0x00197D, 0x018042, - 0x00117D, 0x31804A, 0x10000A, 0x003124, - 0x01280D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x300102, 0x003124, 0x000424, 0x000424, - 0x001224, 0x280502, 0x001A4C, 0x130186, - 0x700002, 0x00002D, 0x030000, 0x00387D, - 0x018042, 0x10000A, 0x132A06, 0x002124, - 0x0000AD, 0x100002, 0x00010D, 0x000924, - 0x006B24, 0x01368D, 0x00397D, 0x000820, - 0x058040, 0x038042, 0x09844A, 0x000606, - 0x08040A, 0x003264, 0x00008D, 0x000A24, - 0x001020, 0x00227D, 0x018040, 0x013C0D, - 0x000810, 0x08043A, 0x29D206, 0x000007, - 0x002820, 0x00207D, 0x018040, 0x00117D, - 0x038042, 0x13804A, 0x33800A, 0x00387D, - 0x018042, 0x08000A, 0x000904, 0x163A86, - 0x000007, 0x00008D, 0x030964, 0x01478D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x380102, - 0x000424, 0x000424, 0x001224, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x14A286, - 0x000007, 0x280502, 0x001A4C, 0x163986, - 0x000007, 0x032164, 0x00632C, 0x003DFD, - 0x018042, 0x08000A, 0x000095, 0x090904, - 0x000007, 0x000820, 0x001A4C, 0x156186, - 0x018040, 0x030000, 0x157A06, 0x002124, - 0x00010D, 0x000924, 0x006B24, 0x015B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x003A64, - 0x000095, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x01628D, 0x000810, 0x08043A, 0x29D206, - 0x000007, 0x14D206, 0x000007, 0x007020, - 0x08010A, 0x10012A, 0x0020FD, 0x038860, - 0x039060, 0x018040, 0x00227D, 0x018042, - 0x003DFD, 0x08000A, 0x31844A, 0x000904, - 0x16D886, 0x18008B, 0x00008D, 0x189904, - 0x00312C, 0x17AA06, 0x000007, 0x00324C, - 0x173386, 0x000007, 0x001904, 0x173086, - 0x000007, 0x000095, 0x199144, 0x00222C, - 0x003124, 0x00636C, 0x000E3D, 0x001375, - 0x000BFD, 0x010042, 0x09804A, 0x10000A, - 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, - 0x000007, 0x00008D, 0x189904, 0x00226C, - 0x00322C, 0x30050A, 0x301DAB, 0x002083, - 0x0018FD, 0x018042, 0x08000A, 0x018924, - 0x300502, 0x001083, 0x001875, 0x010042, - 0x10000A, 0x00008D, 0x010924, 0x001375, - 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, - 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, - 0x305C8B, 0x006083, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x187A86, 0x000007, - 0x001E2D, 0x0005FD, 0x018042, 0x08000A, - 0x028924, 0x280502, 0x00060D, 0x000810, - 0x280C3A, 0x00008D, 0x000810, 0x28143A, - 0x0A808D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001275, 0x030042, 0x21004A, - 0x00008D, 0x1A0944, 0x000007, 0x01980D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0001F5, 0x030042, 0x0D004A, 0x10000A, - 0x089144, 0x000007, 0x000820, 0x010040, - 0x0025F5, 0x0A3144, 0x000007, 0x000820, - 0x032860, 0x030040, 0x00217D, 0x038042, - 0x0B804A, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00008D, 0x000124, 0x00012C, - 0x000E64, 0x001A64, 0x00636C, 0x08010A, - 0x10012A, 0x000820, 0x031060, 0x030040, - 0x0020FD, 0x018042, 0x08000A, 0x00227D, - 0x018042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00197D, 0x018042, 0x08000A, - 0x0022FD, 0x038042, 0x10000A, 0x000820, - 0x031060, 0x030040, 0x090D04, 0x000007, - 0x000820, 0x030040, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x038042, 0x13804A, 0x19804A, 0x110D04, - 0x198D04, 0x000007, 0x08000A, 0x001020, - 0x031860, 0x030860, 0x030040, 0x00008D, - 0x0B0944, 0x000007, 0x000820, 0x010040, - 0x0005F5, 0x030042, 0x08000A, 0x000820, - 0x010040, 0x0000F5, 0x010042, 0x08000A, - 0x000904, 0x1C6086, 0x001E75, 0x030042, - 0x01044A, 0x000C0A, 0x1C7206, 0x000007, - 0x000402, 0x000C02, 0x00177D, 0x001AF5, - 0x018042, 0x03144A, 0x031C4A, 0x03244A, - 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, - 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, - 0x030042, 0x0B004A, 0x1B804A, 0x13804A, - 0x20000A, 0x089144, 0x19A144, 0x0389E4, - 0x0399EC, 0x005502, 0x005D0A, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x006502, 0x006D0A, 0x030042, 0x0B004A, - 0x19004A, 0x2B804A, 0x13804A, 0x21804A, - 0x30000A, 0x089144, 0x19A144, 0x2AB144, - 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, - 0x03A9E4, 0x000702, 0x00107D, 0x000415, - 0x018042, 0x08000A, 0x0109E4, 0x000F02, - 0x002AF5, 0x0019FD, 0x010042, 0x09804A, - 0x10000A, 0x000934, 0x001674, 0x0029F5, - 0x010042, 0x10000A, 0x00917C, 0x002075, - 0x010042, 0x08000A, 0x000904, 0x1ED286, - 0x0026F5, 0x0027F5, 0x030042, 0x09004A, - 0x10000A, 0x000A3C, 0x00167C, 0x001A75, - 0x000BFD, 0x010042, 0x51804A, 0x48000A, - 0x160007, 0x001075, 0x010042, 0x282C0A, - 0x281D12, 0x282512, 0x001F32, 0x1E0007, - 0x0E0007, 0x001975, 0x010042, 0x002DF5, - 0x0D004A, 0x10000A, 0x009144, 0x1FB286, - 0x010042, 0x28340A, 0x000E5D, 0x00008D, - 0x000375, 0x000820, 0x010040, 0x05D2F4, - 0x54D104, 0x00735C, 0x205386, 0x000007, - 0x0C0007, 0x080007, 0x0A0007, 0x02040D, - 0x000810, 0x08043A, 0x332206, 0x000007, - 0x205A06, 0x000007, 0x080007, 0x002275, - 0x010042, 0x20000A, 0x002104, 0x212086, - 0x001E2D, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x209286, 0x000007, 0x002010, - 0x30043A, 0x00057D, 0x0180C3, 0x08000A, - 0x028924, 0x280502, 0x280C02, 0x0A810D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x0004FD, 0x018042, 0x70000A, 0x030000, - 0x007020, 0x06FA06, 0x018040, 0x02180D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x218A86, 0x000007, 0x01F206, 0x000007, - 0x000875, 0x0009FD, 0x00010D, 0x220A06, - 0x000295, 0x000B75, 0x00097D, 0x00000D, - 0x000515, 0x010042, 0x18000A, 0x001904, - 0x287886, 0x0006F5, 0x001020, 0x010040, - 0x0004F5, 0x000820, 0x010040, 0x000775, - 0x010042, 0x09804A, 0x10000A, 0x001124, - 0x000904, 0x22BA86, 0x000815, 0x080102, - 0x101204, 0x22DA06, 0x000575, 0x081204, - 0x000007, 0x100102, 0x000575, 0x000425, - 0x021124, 0x100102, 0x000820, 0x031060, - 0x010040, 0x001924, 0x287886, 0x00008D, - 0x000464, 0x009D04, 0x278886, 0x180102, - 0x000575, 0x010042, 0x28040A, 0x00018D, - 0x000924, 0x280D02, 0x00000D, 0x000924, - 0x281502, 0x10000D, 0x000820, 0x0002F5, - 0x010040, 0x200007, 0x001175, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x23C286, - 0x000007, 0x000100, 0x080B20, 0x130B60, - 0x1B0B60, 0x030A60, 0x010040, 0x050042, - 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, - 0x0006F5, 0x010042, 0x28140A, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x24CA86, 0x004015, 0x000095, 0x010D04, - 0x24B886, 0x100022, 0x10002A, 0x24E206, - 0x000007, 0x333104, 0x2AA904, 0x000007, - 0x032124, 0x280502, 0x001124, 0x000424, - 0x000424, 0x003224, 0x00292C, 0x00636C, - 0x25F386, 0x000007, 0x02B164, 0x000464, - 0x000464, 0x00008D, 0x000A64, 0x280D02, - 0x10008D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x00008D, 0x38B904, 0x000007, - 0x03296C, 0x30010A, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x25BA86, 0x000007, - 0x02312C, 0x28050A, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x267A86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x26C086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x26CA86, 0x000007, 0x003124, 0x300502, - 0x003924, 0x300583, 0x000883, 0x0005F5, - 0x010042, 0x28040A, 0x00008D, 0x008124, - 0x280D02, 0x00008D, 0x008124, 0x281502, - 0x10018D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001025, 0x000575, 0x030042, - 0x09004A, 0x10000A, 0x0A0904, 0x121104, - 0x000007, 0x001020, 0x050860, 0x050040, - 0x0006FD, 0x018042, 0x09004A, 0x10000A, - 0x0000A5, 0x0A0904, 0x121104, 0x000007, - 0x000820, 0x019060, 0x010040, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x284286, - 0x000007, 0x230A06, 0x000007, 0x000606, - 0x000007, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x289286, 0x000007, 0x000100, - 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, - 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, - 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, - 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, - 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, - 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, - 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, - 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, - 0x000606, 0x018040, 0x00008D, 0x000A64, - 0x280D02, 0x000A24, 0x00027D, 0x018042, - 0x10000A, 0x001224, 0x0003FD, 0x018042, - 0x08000A, 0x000904, 0x2A8286, 0x000007, - 0x00018D, 0x000A24, 0x000464, 0x000464, - 0x080102, 0x000924, 0x000424, 0x000424, - 0x100102, 0x02000D, 0x009144, 0x2AD986, - 0x000007, 0x0001FD, 0x018042, 0x08000A, - 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x00027D, 0x001020, 0x000606, 0x018040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x2B2A86, 0x000007, 0x00037D, 0x018042, - 0x08000A, 0x000904, 0x2B5A86, 0x000007, - 0x000075, 0x002E7D, 0x010042, 0x0B804A, - 0x000020, 0x000904, 0x000686, 0x010040, - 0x31844A, 0x30048B, 0x000883, 0x00008D, - 0x000810, 0x28143A, 0x00008D, 0x000810, - 0x280C3A, 0x000675, 0x010042, 0x08000A, - 0x003815, 0x010924, 0x280502, 0x0B000D, - 0x000820, 0x0002F5, 0x010040, 0x000606, - 0x220007, 0x000464, 0x000464, 0x000606, - 0x000007, 0x000134, 0x007F8D, 0x00093C, - 0x281D12, 0x282512, 0x001F32, 0x0E0007, - 0x00010D, 0x00037D, 0x000820, 0x018040, - 0x05D2F4, 0x000007, 0x080007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2D0286, - 0x000007, 0x000606, 0x000007, 0x000007, - 0x000012, 0x100007, 0x320007, 0x600007, - 0x100080, 0x48001A, 0x004904, 0x2D6186, - 0x000007, 0x001210, 0x58003A, 0x000145, - 0x5C5D04, 0x000007, 0x000080, 0x48001A, - 0x004904, 0x2DB186, 0x000007, 0x001210, - 0x50003A, 0x005904, 0x2E0886, 0x000045, - 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, - 0x004224, 0x500102, 0x200502, 0x000082, - 0x40001A, 0x004104, 0x2E3986, 0x000007, - 0x003865, 0x40001A, 0x004020, 0x00104D, - 0x04C184, 0x301B86, 0x000040, 0x040007, - 0x000165, 0x000145, 0x004020, 0x000040, - 0x000765, 0x080080, 0x40001A, 0x004104, - 0x2EC986, 0x000007, 0x001210, 0x40003A, - 0x004104, 0x2F2286, 0x00004D, 0x0000CD, - 0x004810, 0x20043A, 0x000882, 0x40001A, - 0x004104, 0x2F3186, 0x000007, 0x004820, - 0x005904, 0x300886, 0x000040, 0x0007E5, - 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, - 0x4216E0, 0x021260, 0x000040, 0x000032, - 0x400075, 0x00007D, 0x07D574, 0x200512, - 0x000082, 0x40001A, 0x004104, 0x2FE186, - 0x000007, 0x037206, 0x640007, 0x060007, - 0x0000E5, 0x000020, 0x000040, 0x000A65, - 0x000020, 0x020040, 0x020040, 0x000040, - 0x000165, 0x000042, 0x70000A, 0x007104, - 0x30A286, 0x000007, 0x018206, 0x640007, - 0x050000, 0x007020, 0x000040, 0x037206, - 0x640007, 0x000007, 0x00306D, 0x028860, - 0x029060, 0x08000A, 0x028860, 0x008040, - 0x100012, 0x00100D, 0x009184, 0x314186, - 0x000E0D, 0x009184, 0x325186, 0x000007, - 0x300007, 0x001020, 0x003B6D, 0x008040, - 0x000080, 0x08001A, 0x000904, 0x316186, - 0x000007, 0x001220, 0x000DED, 0x008040, - 0x008042, 0x10000A, 0x40000D, 0x109544, - 0x000007, 0x001020, 0x000DED, 0x008040, - 0x008042, 0x20040A, 0x000082, 0x08001A, - 0x000904, 0x31F186, 0x000007, 0x003B6D, - 0x008042, 0x08000A, 0x000E15, 0x010984, - 0x329B86, 0x600007, 0x08001A, 0x000C15, - 0x010984, 0x328386, 0x000020, 0x1A0007, - 0x0002ED, 0x008040, 0x620007, 0x00306D, - 0x028042, 0x0A804A, 0x000820, 0x0A804A, - 0x000606, 0x10804A, 0x000007, 0x282512, - 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, - 0x000786, 0x000007, 0x0C0007, 0x0A0007, - 0x1C0007, 0x003465, 0x020040, 0x004820, - 0x025060, 0x40000A, 0x024060, 0x000040, - 0x454944, 0x000007, 0x004020, 0x003AE5, - 0x000040, 0x0028E5, 0x000042, 0x48000A, - 0x004904, 0x386886, 0x002C65, 0x000042, - 0x40000A, 0x0000D5, 0x454104, 0x000007, - 0x000655, 0x054504, 0x34F286, 0x0001D5, - 0x054504, 0x34F086, 0x002B65, 0x000042, - 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, - 0x000007, 0x454504, 0x000007, 0x0000CD, - 0x444944, 0x000007, 0x454504, 0x000007, - 0x00014D, 0x554944, 0x000007, 0x045144, - 0x34E986, 0x002C65, 0x000042, 0x48000A, - 0x4CD104, 0x000007, 0x04C144, 0x34F386, - 0x000007, 0x160007, 0x002CE5, 0x040042, - 0x40000A, 0x004020, 0x000040, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x356086, - 0x000007, 0x002402, 0x36A206, 0x005C02, - 0x0025E5, 0x000042, 0x40000A, 0x004274, - 0x002AE5, 0x000042, 0x40000A, 0x004274, - 0x500112, 0x0029E5, 0x000042, 0x40000A, - 0x004234, 0x454104, 0x000007, 0x004020, - 0x000040, 0x003EE5, 0x000020, 0x000040, - 0x002DE5, 0x400152, 0x50000A, 0x045144, - 0x364A86, 0x0000C5, 0x003EE5, 0x004020, - 0x000040, 0x002BE5, 0x000042, 0x40000A, - 0x404254, 0x000007, 0x002AE5, 0x004020, - 0x000040, 0x500132, 0x040134, 0x005674, - 0x0029E5, 0x020042, 0x42000A, 0x000042, - 0x50000A, 0x05417C, 0x0028E5, 0x000042, - 0x48000A, 0x0000C5, 0x4CC144, 0x371086, - 0x0026E5, 0x0027E5, 0x020042, 0x40004A, - 0x50000A, 0x00423C, 0x00567C, 0x0028E5, - 0x004820, 0x000040, 0x281D12, 0x282512, - 0x001F72, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x37AA86, 0x0E0007, 0x160007, - 0x1E0007, 0x003EE5, 0x000042, 0x40000A, - 0x004104, 0x37E886, 0x002D65, 0x000042, - 0x28340A, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, - 0x54D104, 0x00735C, 0x385186, 0x000007, - 0x000606, 0x080007, 0x0C0007, 0x080007, - 0x0A0007, 0x0001E5, 0x020045, 0x004020, - 0x000060, 0x000365, 0x000040, 0x002E65, - 0x001A20, 0x0A1A60, 0x000040, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x000606, 0x50004A, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -// -------------------------------------------- -// DS-1E Controller InstructionRAM Code -// 1999/06/21 -// Buf441 slot is Enabled. -// -------------------------------------------- -// 04/09 creat -// 04/12 stop nise fix -// 06/21 WorkingOff timming -static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x00800D, 0x000810, 0x20043A, 0x001A82, - 0x03460D, 0x000810, 0x10043A, 0x02EC0D, - 0x000810, 0x18043A, 0x00010D, 0x020015, - 0x0000FD, 0x000020, 0x038860, 0x039060, - 0x038060, 0x038040, 0x038040, 0x038040, - 0x018040, 0x000A7D, 0x038040, 0x038040, - 0x018040, 0x200402, 0x000882, 0x08001A, - 0x000904, 0x017186, 0x000007, 0x260007, - 0x400007, 0x000007, 0x03258D, 0x000810, - 0x18043A, 0x260007, 0x284402, 0x00087D, - 0x018042, 0x00160A, 0x05A206, 0x000007, - 0x440007, 0x00230D, 0x000810, 0x08043A, - 0x22FA06, 0x000007, 0x0007FD, 0x018042, - 0x08000A, 0x000904, 0x02AB86, 0x000195, - 0x090D04, 0x000007, 0x000820, 0x0000F5, - 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, - 0x018040, 0x000A7D, 0x038042, 0x13804A, - 0x18000A, 0x001820, 0x059060, 0x058860, - 0x018040, 0x0000FD, 0x018042, 0x70000A, - 0x000115, 0x071144, 0x033B86, 0x030000, - 0x007020, 0x036206, 0x018040, 0x00360D, - 0x000810, 0x08043A, 0x232206, 0x000007, - 0x02EC0D, 0x000810, 0x18043A, 0x019A06, - 0x000007, 0x240007, 0x000F8D, 0x000810, - 0x00163A, 0x002402, 0x005C02, 0x0028FD, - 0x000020, 0x018040, 0x08000D, 0x000815, - 0x510984, 0x000007, 0x00004D, 0x000E5D, - 0x000E02, 0x00430D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x00008D, 0x000924, - 0x000F02, 0x00470D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x480480, 0x001210, - 0x28043A, 0x00778D, 0x000810, 0x280C3A, - 0x00068D, 0x000810, 0x28143A, 0x284402, - 0x03258D, 0x000810, 0x18043A, 0x07FF8D, - 0x000820, 0x0002FD, 0x018040, 0x260007, - 0x200007, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x051286, 0x000007, 0x240007, - 0x02EC0D, 0x000810, 0x18043A, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x019B86, 0x000007, 0x01B206, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x22B886, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x065A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x036186, 0x000007, 0x002104, 0x036186, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x07C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00788D, 0x000810, 0x08043A, 0x2A1206, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x070206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x229086, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x225886, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x08DA86, 0x00057D, 0x002820, - 0x03B060, 0x08F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x08FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x009D8D, 0x000810, - 0x08043A, 0x2A1206, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x0A5186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x00107D, 0x018042, - 0x08000A, 0x000215, 0x010984, 0x3A8186, - 0x000007, 0x007FBD, 0x383DC4, 0x000007, - 0x001A7D, 0x001375, 0x018042, 0x09004A, - 0x10000A, 0x0B8D04, 0x139504, 0x000007, - 0x000820, 0x019060, 0x001104, 0x225886, - 0x010040, 0x0017FD, 0x018042, 0x08000A, - 0x000904, 0x225A86, 0x000007, 0x00197D, - 0x038042, 0x09804A, 0x10000A, 0x000924, - 0x001664, 0x0011FD, 0x038042, 0x2B804A, - 0x19804A, 0x00008D, 0x218944, 0x000007, - 0x002244, 0x0C1986, 0x000007, 0x001A64, - 0x002A24, 0x00197D, 0x080102, 0x100122, - 0x000820, 0x039060, 0x018040, 0x003DFD, - 0x00008D, 0x000820, 0x018040, 0x001375, - 0x001A7D, 0x010042, 0x09804A, 0x10000A, - 0x00021D, 0x0189E4, 0x2992E4, 0x309144, - 0x000007, 0x00060D, 0x000A15, 0x000C1D, - 0x001025, 0x00A9E4, 0x012BE4, 0x000464, - 0x01B3E4, 0x0232E4, 0x000464, 0x000464, - 0x000464, 0x000464, 0x00040D, 0x08B1C4, - 0x000007, 0x000820, 0x000BF5, 0x030040, - 0x00197D, 0x038042, 0x09804A, 0x000A24, - 0x08000A, 0x080E64, 0x000007, 0x100122, - 0x000820, 0x031060, 0x010040, 0x0064AC, - 0x00027D, 0x000020, 0x018040, 0x00107D, - 0x018042, 0x0011FD, 0x3B804A, 0x09804A, - 0x20000A, 0x000095, 0x1A1144, 0x00A144, - 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, - 0x0018FD, 0x018042, 0x0010FD, 0x09804A, - 0x28000A, 0x000095, 0x010924, 0x002A64, - 0x0E4986, 0x000007, 0x002904, 0x0E5A86, - 0x000007, 0x0E6206, 0x080002, 0x00008D, - 0x00387D, 0x000820, 0x018040, 0x00127D, - 0x018042, 0x10000A, 0x003904, 0x0F0986, - 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, - 0x000025, 0x0FB206, 0x00002D, 0x000015, - 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, - 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, - 0x400025, 0x00008D, 0x110944, 0x000007, - 0x00018D, 0x109504, 0x000007, 0x009164, - 0x000424, 0x000424, 0x000424, 0x100102, - 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, - 0x00018D, 0x00042D, 0x00008D, 0x109504, - 0x000007, 0x00020D, 0x109184, 0x000007, - 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, - 0x018040, 0x003BFD, 0x001020, 0x03A860, - 0x000815, 0x313184, 0x212184, 0x000007, - 0x03B060, 0x03A060, 0x018040, 0x0022FD, - 0x000095, 0x010924, 0x000424, 0x000424, - 0x001264, 0x100102, 0x000820, 0x039060, - 0x018040, 0x001924, 0x010F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x000424, 0x000424, - 0x00117D, 0x018042, 0x08000A, 0x000A24, - 0x280502, 0x280C02, 0x09800D, 0x000820, - 0x0002FD, 0x018040, 0x200007, 0x0022FD, - 0x018042, 0x08000A, 0x000095, 0x280DC4, - 0x011924, 0x00197D, 0x018042, 0x0011FD, - 0x09804A, 0x10000A, 0x0000B5, 0x113144, - 0x0A8D04, 0x000007, 0x080A44, 0x129504, - 0x000007, 0x0023FD, 0x001020, 0x038040, - 0x101244, 0x000007, 0x000820, 0x039060, - 0x018040, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x123286, 0x000007, 0x003BFD, - 0x000100, 0x000A10, 0x0B807A, 0x13804A, - 0x090984, 0x000007, 0x000095, 0x013D04, - 0x12B886, 0x10000A, 0x100002, 0x090984, - 0x000007, 0x038042, 0x11804A, 0x090D04, - 0x000007, 0x10000A, 0x090D84, 0x000007, - 0x00257D, 0x000820, 0x018040, 0x00010D, - 0x000810, 0x28143A, 0x00127D, 0x018042, - 0x20000A, 0x00197D, 0x018042, 0x00117D, - 0x31804A, 0x10000A, 0x003124, 0x013B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x300102, - 0x003124, 0x000424, 0x000424, 0x001224, - 0x280502, 0x001A4C, 0x143986, 0x700002, - 0x00002D, 0x030000, 0x00387D, 0x018042, - 0x10000A, 0x146206, 0x002124, 0x0000AD, - 0x100002, 0x00010D, 0x000924, 0x006B24, - 0x014A0D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x003264, 0x00008D, 0x000A24, 0x001020, - 0x00227D, 0x018040, 0x014F8D, 0x000810, - 0x08043A, 0x2B5A06, 0x000007, 0x002820, - 0x00207D, 0x018040, 0x00117D, 0x038042, - 0x13804A, 0x33800A, 0x00387D, 0x018042, - 0x08000A, 0x000904, 0x177286, 0x000007, - 0x00008D, 0x030964, 0x015B0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x380102, 0x000424, - 0x000424, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x280502, 0x001A4C, 0x177186, 0x000007, - 0x032164, 0x00632C, 0x003DFD, 0x018042, - 0x08000A, 0x000095, 0x090904, 0x000007, - 0x000820, 0x001A4C, 0x169986, 0x018040, - 0x030000, 0x16B206, 0x002124, 0x00010D, - 0x000924, 0x006B24, 0x016F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x003A64, 0x000095, - 0x001224, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x171286, 0x000007, 0x01760D, - 0x000810, 0x08043A, 0x2B5A06, 0x000007, - 0x160A06, 0x000007, 0x007020, 0x08010A, - 0x10012A, 0x0020FD, 0x038860, 0x039060, - 0x018040, 0x00227D, 0x018042, 0x003DFD, - 0x08000A, 0x31844A, 0x000904, 0x181086, - 0x18008B, 0x00008D, 0x189904, 0x00312C, - 0x18E206, 0x000007, 0x00324C, 0x186B86, - 0x000007, 0x001904, 0x186886, 0x000007, - 0x000095, 0x199144, 0x00222C, 0x003124, - 0x00636C, 0x000E3D, 0x001375, 0x000BFD, - 0x010042, 0x09804A, 0x10000A, 0x038AEC, - 0x0393EC, 0x00224C, 0x18E186, 0x000007, - 0x00008D, 0x189904, 0x00226C, 0x00322C, - 0x30050A, 0x301DAB, 0x002083, 0x0018FD, - 0x018042, 0x08000A, 0x018924, 0x300502, - 0x001083, 0x001875, 0x010042, 0x10000A, - 0x00008D, 0x010924, 0x001375, 0x330542, - 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, - 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, - 0x006083, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x19B286, 0x000007, 0x001E2D, - 0x0005FD, 0x018042, 0x08000A, 0x028924, - 0x280502, 0x00060D, 0x000810, 0x280C3A, - 0x00008D, 0x000810, 0x28143A, 0x0A808D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x001275, 0x030042, 0x21004A, 0x00008D, - 0x1A0944, 0x000007, 0x01AB8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, - 0x030042, 0x0D004A, 0x10000A, 0x089144, - 0x000007, 0x000820, 0x010040, 0x0025F5, - 0x0A3144, 0x000007, 0x000820, 0x032860, - 0x030040, 0x00217D, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00008D, 0x000124, 0x00012C, 0x000E64, - 0x001A64, 0x00636C, 0x08010A, 0x10012A, - 0x000820, 0x031060, 0x030040, 0x0020FD, - 0x018042, 0x08000A, 0x00227D, 0x018042, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00197D, 0x018042, 0x08000A, 0x0022FD, - 0x038042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x090D04, 0x000007, 0x000820, - 0x030040, 0x038042, 0x0B804A, 0x10000A, - 0x000820, 0x031060, 0x030040, 0x038042, - 0x13804A, 0x19804A, 0x110D04, 0x198D04, - 0x000007, 0x08000A, 0x001020, 0x031860, - 0x030860, 0x030040, 0x00008D, 0x0B0944, - 0x000007, 0x000820, 0x010040, 0x0005F5, - 0x030042, 0x08000A, 0x000820, 0x010040, - 0x0000F5, 0x010042, 0x08000A, 0x000904, - 0x1D9886, 0x001E75, 0x030042, 0x01044A, - 0x000C0A, 0x1DAA06, 0x000007, 0x000402, - 0x000C02, 0x00177D, 0x001AF5, 0x018042, - 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, - 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, - 0x00043D, 0x0013F5, 0x001AFD, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x005502, 0x005D0A, 0x030042, 0x0B004A, - 0x1B804A, 0x13804A, 0x20000A, 0x089144, - 0x19A144, 0x0389E4, 0x0399EC, 0x006502, - 0x006D0A, 0x030042, 0x0B004A, 0x19004A, - 0x2B804A, 0x13804A, 0x21804A, 0x30000A, - 0x089144, 0x19A144, 0x2AB144, 0x0389E4, - 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, - 0x000702, 0x00107D, 0x000415, 0x018042, - 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, - 0x0019FD, 0x010042, 0x09804A, 0x10000A, - 0x000934, 0x001674, 0x0029F5, 0x010042, - 0x10000A, 0x00917C, 0x002075, 0x010042, - 0x08000A, 0x000904, 0x200A86, 0x0026F5, - 0x0027F5, 0x030042, 0x09004A, 0x10000A, - 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, - 0x010042, 0x51804A, 0x48000A, 0x160007, - 0x001075, 0x010042, 0x282C0A, 0x281D12, - 0x282512, 0x001F32, 0x1E0007, 0x0E0007, - 0x001975, 0x010042, 0x002DF5, 0x0D004A, - 0x10000A, 0x009144, 0x20EA86, 0x010042, - 0x28340A, 0x000E5D, 0x00008D, 0x000375, - 0x000820, 0x010040, 0x05D2F4, 0x54D104, - 0x00735C, 0x218B86, 0x000007, 0x0C0007, - 0x080007, 0x0A0007, 0x02178D, 0x000810, - 0x08043A, 0x34B206, 0x000007, 0x219206, - 0x000007, 0x080007, 0x002275, 0x010042, - 0x20000A, 0x002104, 0x225886, 0x001E2D, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x21CA86, 0x000007, 0x002010, 0x30043A, - 0x00057D, 0x0180C3, 0x08000A, 0x028924, - 0x280502, 0x280C02, 0x0A810D, 0x000820, - 0x0002F5, 0x010040, 0x220007, 0x0004FD, - 0x018042, 0x70000A, 0x030000, 0x007020, - 0x07FA06, 0x018040, 0x022B8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x22C286, - 0x000007, 0x020206, 0x000007, 0x000875, - 0x0009FD, 0x00010D, 0x234206, 0x000295, - 0x000B75, 0x00097D, 0x00000D, 0x000515, - 0x010042, 0x18000A, 0x001904, 0x2A0086, - 0x0006F5, 0x001020, 0x010040, 0x0004F5, - 0x000820, 0x010040, 0x000775, 0x010042, - 0x09804A, 0x10000A, 0x001124, 0x000904, - 0x23F286, 0x000815, 0x080102, 0x101204, - 0x241206, 0x000575, 0x081204, 0x000007, - 0x100102, 0x000575, 0x000425, 0x021124, - 0x100102, 0x000820, 0x031060, 0x010040, - 0x001924, 0x2A0086, 0x00008D, 0x000464, - 0x009D04, 0x291086, 0x180102, 0x000575, - 0x010042, 0x28040A, 0x00018D, 0x000924, - 0x280D02, 0x00000D, 0x000924, 0x281502, - 0x10000D, 0x000820, 0x0002F5, 0x010040, - 0x200007, 0x001175, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x24FA86, 0x000007, - 0x000100, 0x080B20, 0x130B60, 0x1B0B60, - 0x030A60, 0x010040, 0x050042, 0x3D004A, - 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, - 0x010042, 0x28140A, 0x0004F5, 0x010042, - 0x08000A, 0x000315, 0x010D04, 0x260286, - 0x004015, 0x000095, 0x010D04, 0x25F086, - 0x100022, 0x10002A, 0x261A06, 0x000007, - 0x333104, 0x2AA904, 0x000007, 0x032124, - 0x280502, 0x284402, 0x001124, 0x400102, - 0x000424, 0x000424, 0x003224, 0x00292C, - 0x00636C, 0x277386, 0x000007, 0x02B164, - 0x000464, 0x000464, 0x00008D, 0x000A64, - 0x280D02, 0x10008D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x00008D, 0x38B904, - 0x000007, 0x03296C, 0x30010A, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x270286, - 0x000007, 0x00212C, 0x28050A, 0x00316C, - 0x00046C, 0x00046C, 0x28450A, 0x001124, - 0x006B64, 0x100102, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x004124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x27FA86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x284086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x284A86, 0x000007, 0x284402, 0x003124, - 0x300502, 0x003924, 0x300583, 0x000883, - 0x0005F5, 0x010042, 0x28040A, 0x00008D, - 0x008124, 0x280D02, 0x00008D, 0x008124, - 0x281502, 0x10018D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001025, 0x000575, - 0x030042, 0x09004A, 0x10000A, 0x0A0904, - 0x121104, 0x000007, 0x001020, 0x050860, - 0x050040, 0x0006FD, 0x018042, 0x09004A, - 0x10000A, 0x0000A5, 0x0A0904, 0x121104, - 0x000007, 0x000820, 0x019060, 0x010040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x29CA86, 0x000007, 0x244206, 0x000007, - 0x000606, 0x000007, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x2A1A86, 0x000007, - 0x000100, 0x080B20, 0x138B60, 0x1B8B60, - 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, - 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, - 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, - 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, - 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, - 0x038A60, 0x000606, 0x018040, 0x00008D, - 0x000A64, 0x280D02, 0x000A24, 0x00027D, - 0x018042, 0x10000A, 0x001224, 0x0003FD, - 0x018042, 0x08000A, 0x000904, 0x2C0A86, - 0x000007, 0x00018D, 0x000A24, 0x000464, - 0x000464, 0x080102, 0x000924, 0x000424, - 0x000424, 0x100102, 0x02000D, 0x009144, - 0x2C6186, 0x000007, 0x0001FD, 0x018042, - 0x08000A, 0x000A44, 0x2C4386, 0x018042, - 0x0A000D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00027D, 0x001020, 0x000606, - 0x018040, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x2CB286, 0x000007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2CE286, - 0x000007, 0x000075, 0x002E7D, 0x010042, - 0x0B804A, 0x000020, 0x000904, 0x000686, - 0x010040, 0x31844A, 0x30048B, 0x000883, - 0x00008D, 0x000810, 0x28143A, 0x00008D, - 0x000810, 0x280C3A, 0x000675, 0x010042, - 0x08000A, 0x003815, 0x010924, 0x280502, - 0x0B000D, 0x000820, 0x0002F5, 0x010040, - 0x000606, 0x220007, 0x000464, 0x000464, - 0x000606, 0x000007, 0x000134, 0x007F8D, - 0x00093C, 0x281D12, 0x282512, 0x001F32, - 0x0E0007, 0x00010D, 0x00037D, 0x000820, - 0x018040, 0x05D2F4, 0x000007, 0x080007, - 0x00037D, 0x018042, 0x08000A, 0x000904, - 0x2E8A86, 0x000007, 0x000606, 0x000007, - 0x000007, 0x000012, 0x100007, 0x320007, - 0x600007, 0x460007, 0x100080, 0x48001A, - 0x004904, 0x2EF186, 0x000007, 0x001210, - 0x58003A, 0x000145, 0x5C5D04, 0x000007, - 0x000080, 0x48001A, 0x004904, 0x2F4186, - 0x000007, 0x001210, 0x50003A, 0x005904, - 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, - 0x7FFF7D, 0x07D524, 0x004224, 0x500102, - 0x200502, 0x000082, 0x40001A, 0x004104, - 0x2FC986, 0x000007, 0x003865, 0x40001A, - 0x004020, 0x00104D, 0x04C184, 0x31AB86, - 0x000040, 0x040007, 0x000165, 0x000145, - 0x004020, 0x000040, 0x000765, 0x080080, - 0x40001A, 0x004104, 0x305986, 0x000007, - 0x001210, 0x40003A, 0x004104, 0x30B286, - 0x00004D, 0x0000CD, 0x004810, 0x20043A, - 0x000882, 0x40001A, 0x004104, 0x30C186, - 0x000007, 0x004820, 0x005904, 0x319886, - 0x000040, 0x0007E5, 0x200480, 0x2816A0, - 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, - 0x000040, 0x000032, 0x400075, 0x00007D, - 0x07D574, 0x200512, 0x000082, 0x40001A, - 0x004104, 0x317186, 0x000007, 0x038A06, - 0x640007, 0x0000E5, 0x000020, 0x000040, - 0x000A65, 0x000020, 0x020040, 0x020040, - 0x000040, 0x000165, 0x000042, 0x70000A, - 0x007104, 0x323286, 0x000007, 0x060007, - 0x019A06, 0x640007, 0x050000, 0x007020, - 0x000040, 0x038A06, 0x640007, 0x000007, - 0x00306D, 0x028860, 0x029060, 0x08000A, - 0x028860, 0x008040, 0x100012, 0x00100D, - 0x009184, 0x32D186, 0x000E0D, 0x009184, - 0x33E186, 0x000007, 0x300007, 0x001020, - 0x003B6D, 0x008040, 0x000080, 0x08001A, - 0x000904, 0x32F186, 0x000007, 0x001220, - 0x000DED, 0x008040, 0x008042, 0x10000A, - 0x40000D, 0x109544, 0x000007, 0x001020, - 0x000DED, 0x008040, 0x008042, 0x20040A, - 0x000082, 0x08001A, 0x000904, 0x338186, - 0x000007, 0x003B6D, 0x008042, 0x08000A, - 0x000E15, 0x010984, 0x342B86, 0x600007, - 0x08001A, 0x000C15, 0x010984, 0x341386, - 0x000020, 0x1A0007, 0x0002ED, 0x008040, - 0x620007, 0x00306D, 0x028042, 0x0A804A, - 0x000820, 0x0A804A, 0x000606, 0x10804A, - 0x000007, 0x282512, 0x001F32, 0x05D2F4, - 0x54D104, 0x00735C, 0x000786, 0x000007, - 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, - 0x020040, 0x004820, 0x025060, 0x40000A, - 0x024060, 0x000040, 0x454944, 0x000007, - 0x004020, 0x003AE5, 0x000040, 0x0028E5, - 0x000042, 0x48000A, 0x004904, 0x39F886, - 0x002C65, 0x000042, 0x40000A, 0x0000D5, - 0x454104, 0x000007, 0x000655, 0x054504, - 0x368286, 0x0001D5, 0x054504, 0x368086, - 0x002B65, 0x000042, 0x003AE5, 0x50004A, - 0x40000A, 0x45C3D4, 0x000007, 0x454504, - 0x000007, 0x0000CD, 0x444944, 0x000007, - 0x454504, 0x000007, 0x00014D, 0x554944, - 0x000007, 0x045144, 0x367986, 0x002C65, - 0x000042, 0x48000A, 0x4CD104, 0x000007, - 0x04C144, 0x368386, 0x000007, 0x160007, - 0x002CE5, 0x040042, 0x40000A, 0x004020, - 0x000040, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x36F086, 0x000007, 0x002402, - 0x383206, 0x005C02, 0x0025E5, 0x000042, - 0x40000A, 0x004274, 0x002AE5, 0x000042, - 0x40000A, 0x004274, 0x500112, 0x0029E5, - 0x000042, 0x40000A, 0x004234, 0x454104, - 0x000007, 0x004020, 0x000040, 0x003EE5, - 0x000020, 0x000040, 0x002DE5, 0x400152, - 0x50000A, 0x045144, 0x37DA86, 0x0000C5, - 0x003EE5, 0x004020, 0x000040, 0x002BE5, - 0x000042, 0x40000A, 0x404254, 0x000007, - 0x002AE5, 0x004020, 0x000040, 0x500132, - 0x040134, 0x005674, 0x0029E5, 0x020042, - 0x42000A, 0x000042, 0x50000A, 0x05417C, - 0x0028E5, 0x000042, 0x48000A, 0x0000C5, - 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, - 0x020042, 0x40004A, 0x50000A, 0x00423C, - 0x00567C, 0x0028E5, 0x004820, 0x000040, - 0x281D12, 0x282512, 0x001F72, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x393A86, - 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, - 0x000042, 0x40000A, 0x004104, 0x397886, - 0x002D65, 0x000042, 0x28340A, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, - 0x39E186, 0x000007, 0x000606, 0x080007, - 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, - 0x020045, 0x004020, 0x000060, 0x000365, - 0x000040, 0x002E65, 0x001A20, 0x0A1A60, - 0x000040, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x000606, 0x50004A, - 0x0017FD, 0x018042, 0x08000A, 0x000904, - 0x225A86, 0x000007, 0x00107D, 0x018042, - 0x0011FD, 0x33804A, 0x19804A, 0x20000A, - 0x000095, 0x2A1144, 0x01A144, 0x3B9086, - 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, - 0x018042, 0x0010FD, 0x09804A, 0x38000A, - 0x000095, 0x010924, 0x003A64, 0x3B8186, - 0x000007, 0x003904, 0x3B9286, 0x000007, - 0x3B9A06, 0x00000D, 0x00008D, 0x000820, - 0x00387D, 0x018040, 0x700002, 0x00117D, - 0x018042, 0x00197D, 0x29804A, 0x30000A, - 0x380002, 0x003124, 0x000424, 0x000424, - 0x002A24, 0x280502, 0x00068D, 0x000810, - 0x28143A, 0x00750D, 0x00B124, 0x002264, - 0x3D0386, 0x284402, 0x000810, 0x280C3A, - 0x0B800D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00758D, 0x00B124, 0x100102, - 0x012144, 0x3E4986, 0x001810, 0x10003A, - 0x00387D, 0x018042, 0x08000A, 0x000904, - 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, - 0x00008D, 0x023164, 0x000A64, 0x280D02, - 0x0B808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00387D, 0x018042, 0x08000A, - 0x000904, 0x3E3286, 0x030000, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x3D8286, - 0x000007, 0x002810, 0x28043A, 0x00750D, - 0x030924, 0x002264, 0x280D02, 0x02316C, - 0x28450A, 0x0B810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x00008D, 0x000A24, - 0x3E4A06, 0x100102, 0x001810, 0x10003A, - 0x0000BD, 0x003810, 0x30043A, 0x00187D, - 0x018042, 0x0018FD, 0x09804A, 0x20000A, - 0x0000AD, 0x028924, 0x07212C, 0x001010, - 0x300583, 0x300D8B, 0x3014BB, 0x301C83, - 0x002083, 0x00137D, 0x038042, 0x33844A, - 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, - 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, - 0x001E0D, 0x0005FD, 0x018042, 0x20000A, - 0x020924, 0x00068D, 0x00A96C, 0x00009D, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x3F6A86, 0x000007, 0x280502, 0x280D0A, - 0x284402, 0x001810, 0x28143A, 0x0C008D, - 0x000820, 0x0002FD, 0x018040, 0x220007, - 0x003904, 0x225886, 0x001E0D, 0x00057D, - 0x018042, 0x20000A, 0x020924, 0x0000A5, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x402A86, 0x000007, 0x280502, 0x280C02, - 0x002010, 0x28143A, 0x0C010D, 0x000820, - 0x0002FD, 0x018040, 0x225A06, 0x220007, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -#endif //_HWMCODE_ diff --git a/sound/oss/yss225.c b/sound/oss/yss225.c deleted file mode 100644 index e700400576..0000000000 --- a/sound/oss/yss225.c +++ /dev/null @@ -1,319 +0,0 @@ -#include - -unsigned char page_zero[] __initdata = { -0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, -0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, -0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, -0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, -0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, -0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, -0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, -0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, -0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, -0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, -0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, -0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, -0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, -0x1d, 0x02, 0xdf -}; - -unsigned char page_one[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, -0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, -0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, -0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, -0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, -0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, -0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, -0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, -0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, -0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, -0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, -0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, -0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, -0x60, 0x00, 0x1b -}; - -unsigned char page_two[] __initdata = { -0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, -0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, -0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, -0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, -0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, -0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, -0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, -0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 -}; - -unsigned char page_three[] __initdata = { -0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, -0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, -0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, -0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, -0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, -0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 -}; - -unsigned char page_four[] __initdata = { -0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, -0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, -0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, -0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, -0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, -0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 -}; - -unsigned char page_six[] __initdata = { -0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, -0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, -0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, -0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, -0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, -0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, -0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, -0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, -0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, -0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, -0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, -0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, -0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, -0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, -0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, -0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, -0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, -0x80, 0x00, 0x7e, 0x80, 0x80 -}; - -unsigned char page_seven[] __initdata = { -0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, -0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, -0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, -0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, -0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, -0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, -0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, -0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, -0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, -0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, -0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, -0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00 -}; - -unsigned char page_zero_v2[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -unsigned char page_one_v2[] __initdata = { -0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -unsigned char page_two_v2[] __initdata = { -0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; -unsigned char page_three_v2[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; -unsigned char page_four_v2[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 -}; - -unsigned char page_seven_v2[] __initdata = { -0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -unsigned char mod_v2[] __initdata = { -0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, -0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, -0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, -0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, -0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, -0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, -0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, -0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, -0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, -0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, -0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, -0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, -0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, -0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, -0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, -0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, -0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, -0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, -0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, -0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, -0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, -0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, -0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, -0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, -0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, -0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, -0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, -0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 -}; -unsigned char coefficients[] __initdata = { -0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, -0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, -0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, -0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, -0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, -0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, -0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, -0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, -0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, -0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, -0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, -0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, -0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, -0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, -0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, -0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, -0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, -0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, -0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, -0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, -0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, -0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, -0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, -0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, -0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, -0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, -0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, -0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, -0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, -0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, -0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, -0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, -0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, -0xba -}; -unsigned char coefficients2[] __initdata = { -0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, -0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, -0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, -0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, -0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 -}; -unsigned char coefficients3[] __initdata = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, -0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, -0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, -0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, -0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, -0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, -0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, -0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, -0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, -0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, -0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, -0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, -0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, -0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, -0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, -0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, -0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, -0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, -0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, -0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, -0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, -0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, -0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, -0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, -0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, -0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, -0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, -0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, -0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, -0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, -0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, -0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, -0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, -0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, -0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, -0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, -0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff -}; - diff --git a/sound/oss/yss225.h b/sound/oss/yss225.h deleted file mode 100644 index 56d8b6b5e4..0000000000 --- a/sound/oss/yss225.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __yss255_h__ -#define __yss255_h__ - -extern unsigned char page_zero[256]; -extern unsigned char page_one[256]; -extern unsigned char page_two[128]; -extern unsigned char page_three[128]; -extern unsigned char page_four[128]; -extern unsigned char page_six[192]; -extern unsigned char page_seven[256]; -extern unsigned char page_zero_v2[96]; -extern unsigned char page_one_v2[96]; -extern unsigned char page_two_v2[48]; -extern unsigned char page_three_v2[48]; -extern unsigned char page_four_v2[48]; -extern unsigned char page_seven_v2[96]; -extern unsigned char mod_v2[304]; -extern unsigned char coefficients[364]; -extern unsigned char coefficients2[56]; -extern unsigned char coefficients3[404]; - - -#endif /* __ys225_h__ */ - diff --git a/sound/sound_core.c b/sound/sound_core.c index 0b0a016ca6..5322c50c96 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -365,25 +365,6 @@ int register_sound_dsp(const struct file_operations *fops, int dev) EXPORT_SYMBOL(register_sound_dsp); -/** - * register_sound_synth - register a synth device - * @fops: File operations for the driver - * @dev: Unit number to allocate - * - * Allocate a synth device. Unit is the number of the synth device requested. - * Pass -1 to request the next free synth unit. On success the allocated - * number is returned, on failure a negative error code is returned. - */ - - -int register_sound_synth(const struct file_operations *fops, int dev) -{ - return sound_insert_unit(&chains[9], fops, dev, 9, 137, - "synth", S_IRUSR | S_IWUSR, NULL); -} - -EXPORT_SYMBOL(register_sound_synth); - /** * unregister_sound_special - unregister a special sound device * @unit: unit number to allocate @@ -449,21 +430,6 @@ void unregister_sound_dsp(int unit) EXPORT_SYMBOL(unregister_sound_dsp); -/** - * unregister_sound_synth - unregister a synth device - * @unit: unit number to allocate - * - * Release a sound device that was allocated with register_sound_synth(). - * The unit passed is the return value from the register function. - */ - -void unregister_sound_synth(int unit) -{ - return sound_remove_unit(&chains[9], unit); -} - -EXPORT_SYMBOL(unregister_sound_synth); - /* * Now our file operations */