diff -N -u -r linux-original/Makefile linux/Makefile --- linux-original/Makefile Sun Jan 7 17:40:54 1996 +++ linux/Makefile Sun Jan 7 10:29:46 1996 @@ -132,6 +132,10 @@ DRIVERS := $(DRIVERS) drivers/pci/pci.a endif +ifdef CONFIG_PCSP +DRIVERS := $(DRIVERS) drivers/pcsnd/pcsp.a +endif + include arch/$(ARCH)/Makefile ifdef SMP @@ -296,7 +300,7 @@ mrproper: clean rm -f include/linux/autoconf.h include/linux/version.h - rm -f drivers/sound/local.h drivers/sound/.defines + rm -f drivers/sound/local.h drivers/sound/.defines drivers/pcsnd/local.h rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash rm -f .version .config* config.in config.old diff -N -u -r linux-original/arch/i386/config.in linux/arch/i386/config.in --- linux-original/arch/i386/config.in Sun Jan 7 17:37:00 1996 +++ linux/arch/i386/config.in Thu Jan 4 06:51:05 1996 @@ -76,6 +76,10 @@ if [ "$CONFIG_SOUND" != "n" ]; then source drivers/sound/Config.in fi +bool 'PC-Speaker and DAC driver support' CONFIG_PCSP n +if [ "$CONFIG_PCSP" = "y" ]; then + ( cd drivers/pcsnd; ./configure ) +fi mainmenu_option next_comment comment 'Kernel hacking' diff -N -u -r linux-original/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- linux-original/arch/i386/kernel/irq.c Mon Oct 30 11:30:17 1995 +++ linux/arch/i386/kernel/irq.c Wed Jan 3 10:26:08 1996 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -420,3 +421,100 @@ request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); } + +#ifdef CONFIG_PCSP +/* + * /dev/pcsp implementation, part of linux/arch/i386/kernel/irq.c + * + * Copyright (C) 1993, 1994 Michael Beck + */ + +static int (*pcsp_IRQ)(void) = NULL; +static const char *pcsp_irq0_owner = NULL; + +/* + * This is a mix of a fast and a slow interrupt; + * pcsp_IRQ is called like a fast int, if it returns a 0, + * the original (slow) handler is started +*/ +extern void FAST_timer_irq(void); +__asm__( + "\n.align 4\n" + "FAST_timer_irq:\n\t" + SAVE_MOST + ACK_FIRST(0x01) + "incl intr_count\n\t" + "sti\n\t" + "call *pcsp_IRQ\n\t" + "cmpl $0,%eax\n\t" + "je 9f\n\t" + "cli\n\t" + UNBLK_FIRST(0x01) + "decl intr_count\n\t" + RESTORE_MOST + + "\n.align 4\n" + "9:\t" /* call the original do_IRQ */ + "popl %ecx\n\t" + "popl %edx\n\t" + "popl %eax\n\t" + "pop %ds\n\t" + "pop %es\n\t" + + "pushl $-2\n\t" /* we know this is IRQ0 */ + SAVE_ALL + "movl %esp,%ebx\n\t" + "pushl %ebx\n\t" + "pushl $0\n\t" + "call do_IRQ\n\t" + "addl $8,%esp\n\t" + "cli\n\t" + UNBLK_FIRST(0x01) + "decl intr_count\n\t" + "jmp ret_from_sys_call\n" +); + +/* + * Set the function func to be executed as fast-timer-int; + * if func returns a 0, the old IRQ0-handler is called + * This is only allowed if IRQ0 was a former SLOW-int; + * should be no problem because do_timer() use it normally :-) + */ +int pcsp_set_irq(int (*func)(void)) +{ + unsigned long flags; + + pcsp_IRQ = func; + if (! pcsp_IRQ) + return -EINVAL; + if (! irq_action->name) + return -EINVAL; + if (irq_action->flags & SA_INTERRUPT) + return -EBUSY; + pcsp_irq0_owner = irq_action->name; + irq_action->name = "pcsp+timer"; + save_flags(flags); + cli(); + set_intr_gate(0x20, FAST_timer_irq); + restore_flags(flags); + return 0; +} + +/* + * reset the IRQ0 to default handling (a SLOW-int) + */ +int pcsp_release_irq(void) +{ + unsigned long flags; + + if (! pcsp_IRQ) + return -EINVAL; + save_flags(flags); + cli(); + set_intr_gate(0x20, interrupt[0]); + restore_flags(flags); + pcsp_IRQ = NULL; + irq_action->name = pcsp_irq0_owner; + return 0; +} +#endif diff -N -u -r linux-original/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c --- linux-original/arch/i386/kernel/time.c Thu Dec 21 20:54:20 1995 +++ linux/arch/i386/kernel/time.c Wed Jan 3 00:47:39 1996 @@ -115,6 +115,10 @@ #define TICK_SIZE tick +#ifdef CONFIG_PCSP +extern int pcsp_clockticks, pcsp_timer0_latch; +#endif + static unsigned long do_slow_gettimeoffset(void) { int count; @@ -124,6 +128,9 @@ outb_p(0x00, 0x43); /* latch the count ASAP */ count = inb_p(0x40); /* read the latched count */ count |= inb(0x40) << 8; +#ifdef CONFIG_PCSP + count = pcsp_clockticks - pcsp_timer0_latch + count; +#endif /* we know probability of underflow is always MUCH less than 1% */ if (count > (LATCH - LATCH/100)) { /* check for pending timer interrupt */ diff -N -u -r linux-original/drivers/Makefile linux/drivers/Makefile --- linux-original/drivers/Makefile Thu Dec 21 20:54:33 1995 +++ linux/drivers/Makefile Wed Jan 3 09:23:39 1996 @@ -9,7 +9,7 @@ SUB_DIRS := block char net #streams MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sound cdrom +ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sound pcsnd cdrom ifdef CONFIG_PCI SUB_DIRS += pci @@ -32,6 +32,10 @@ ifeq ($(CONFIG_SOUND),m) MOD_SUB_DIRS += sound endif +endif + +ifeq ($(CONFIG_PCSP),y) +SUB_DIRS += pcsnd endif ifdef CONFIG_CD_NO_IDESCSI diff -N -u -r linux-original/drivers/char/mem.c linux/drivers/char/mem.c --- linux-original/drivers/char/mem.c Thu Dec 28 14:27:10 1995 +++ linux/drivers/char/mem.c Wed Jan 3 00:49:40 1996 @@ -26,6 +26,10 @@ void soundcard_init(void); #endif +#ifdef CONFIG_PCSP +void pcsp_init(void); +#endif + static int read_ram(struct inode * inode, struct file * file, char * buf, int count) { return -EIO; @@ -390,6 +394,9 @@ #endif #ifdef CONFIG_SOUND soundcard_init(); +#endif +#ifdef CONFIG_PCSP + pcsp_init(); #endif #if CONFIG_QIC02_TAPE qic02_tape_init(); diff -N -u -r linux-original/drivers/char/vt.c linux/drivers/char/vt.c --- linux-original/drivers/char/vt.c Thu Dec 21 20:54:27 1995 +++ linux/drivers/char/vt.c Wed Jan 3 00:47:39 1996 @@ -8,6 +8,7 @@ * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995 */ +#include #include #include #include @@ -146,9 +147,18 @@ * We also return immediately, which is what was implied within the X * comments - KDMKTONE doesn't put the process to sleep. */ + +#ifdef CONFIG_PCSP +extern char pcsp_speaker; +#endif + static void kd_nosound(unsigned long ignored) { +#ifdef CONFIG_PCSP + /* can't allow usage of counter 2 if /dev/pcsp use it */ + if (! pcsp_speaker) +#endif /* disable counter 2 */ outb(inb_p(0x61)&0xFC, 0x61); return; @@ -159,6 +169,10 @@ { static struct timer_list sound_timer = { NULL, NULL, 0, 0, kd_nosound }; +#ifdef CONFIG_PCSP + /* can't allow usage of counter 2 if /dev/pcsp use it */ + if (! pcsp_speaker) { +#endif cli(); del_timer(&sound_timer); if (count) { @@ -177,6 +191,9 @@ } else kd_nosound(0); sti(); +#ifdef CONFIG_PCSP + } +#endif return; } diff -N -u -r linux-original/drivers/pcsnd/COPYING linux/drivers/pcsnd/COPYING --- linux-original/drivers/pcsnd/COPYING Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/COPYING Thu May 4 22:21:49 1995 @@ -0,0 +1,340 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -N -u -r linux-original/drivers/pcsnd/Makefile linux/drivers/pcsnd/Makefile --- linux-original/drivers/pcsnd/Makefile Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/Makefile Wed Jan 3 09:22:47 1996 @@ -0,0 +1,55 @@ +# Makefile for the Linux Alternate-Sound-Driver PCSP +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes. (hopefully) +# +# Stolen from the sound Makefile, Michael Beck - beck@informatik.hu-berlin.de +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + +OBJS = pcsp.o pcsndriv.o pcsp_mixer.o + +all: pcsp.a + +pcsp.a: local.h $(OBJS) + $(AR) rcs pcsp.a $(OBJS) + sync + +config: + @./configure + @./pcspinstall + +local.h: + @./configure + +clean: + rm -f core *.o *.a *.BAK *.BA *.B + for i in *.c;do rm -f `basename $$i .c`.s;done + +indent: + for n in *.c;do echo indent $$n;indent $$n;done + +dep: + $(CPP) -M *.c > .depend + +modules: + + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +include $(TOPDIR)/Rules.make diff -N -u -r linux-original/drivers/pcsnd/README linux/drivers/pcsnd/README --- linux-original/drivers/pcsnd/README Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/README Thu May 4 22:23:55 1995 @@ -0,0 +1,49 @@ +Installing and compiling the driver version 0.9 +----------------------------------------------- + +0) Install the kernel sources (1.2.x) + and the pcsndrv-0.9.tar.z. (You have already done this). + +1) Apply the patch (linux-1.2.x.pcsp.diff) in the /usr/src directory + by patch -p0 < linux-1.2.x.pcsp.diff + if your kernel-version differs from 1.2.0 test this: + main.c : the pcsp_setup entry + sched.c : remove "static" in the definition of do_timer + irq.c : added some stuff at end, + #include + mem.c : include mem_start = pcsp_init(mem_start); + and the extern long pcsp... definition + vt.c : the interior of kd_mksound and kd_nosound must be + enclosed with + if (! pcsp_speaker) { + ... + } + #include + ksyms.c : added X(pcsp_timer0_latch) + X(pcsp...) + +2) Run "make config" in the Linux directory + +------->NOTE: This is NOT a replacement for VoxWare (the real sounddriver). + So don't select 'Sound card support' if you don't have a + Soundcard. + Select only 'PC-Speaker and DAC driver support' !!! + +3) Recompile the kernel. + +To build the device files simply run the shell script pcspinstall +in this directory. It creates: + + /dev/pcsp (and a symbolic link /dev/dsp if you don't have the + sound-driver installed) + /dev/pcaudio (and a symbolic link /dev/audio ...) + /dev/pcmixer (and a symbolic link /dev/mixer ...) + + (if no sound-driver ...) + these only consist of + #include + +Feel free to contact me (flames about my english and the useless of this +driver will be redirected to /dev/null, oh no, it's full...). + +Michael Beck beck@informatik.hu-berlin.de diff -N -u -r linux-original/drivers/pcsnd/configure linux/drivers/pcsnd/configure --- linux-original/drivers/pcsnd/configure Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/configure Fri May 5 19:56:36 1995 @@ -0,0 +1,143 @@ +#!/bin/bash +# +# Configure the PCSP-driver Version 0.9 +# + +cat <local.h +/* + * local.h + * + * /dev/pcsp implementation (dsp & audio for PC Speaker and DACs) + * + * Copyright (C) 1993, 1994, 1995 Michael Beck + * + * ---> This file was generated by configure. Don't edit! <--- + */ + +END + +echo -n "Disable automatic speed detection [y/N] " +read answer +if [ "$answer" = "y" ] ; then + echo "#undef PCSP_TEST_SPEED" >>local.h + echo "This is REALLY a bad idea!" + while true + do + echo -n "Enter a samplerate for PCSP [12500-18356] :" + read answer + answer=${answer:-0} + if [ $answer -le 18356 ] ; then + if [ $answer -ge 12500 ] ; then + break + fi + fi + echo "Must be in the range 12500 - 18356" + done + echo "#define SELECTED_SRATE $answer" >>local.h +else + echo "#define PCSP_TEST_SPEED" >>local.h + echo "#undef SELECTED_SRATE" >>local.h +fi + +cat <>local.h +else + echo "#define PCSP_16BIT_SUPPORT" >>local.h +fi + +if [ "$answer" = "n" ] ; then +cat <>local.h +else + echo "#define PCSP_MIXER" >>local.h + while true + do + echo -n "Left volume at startup (0-100) :" + read left + left=${left:-100} + if [ $left -le 100 ] ; then + if [ $left -ge 0 ] ; then + break + fi + fi + echo "Must be in the range 0 - 100" + done + echo "#define DEFAULT_LEFT $left" >>local.h + while true + do + echo -n "Right volume at startup (0-100) :" + read right + right=${right:-100} + if [ $right -le 100 ] ; then + if [ $right -ge 0 ] ; then + break + fi + fi + echo "Must be in the range 0 - 100" + done + echo "#define DEFAULT_RIGHT $right" >>local.h +fi + +cat < ++ #include ++ #ifdef CONFIG_PCSP ++ #include ++ #endif ++ + #include "joystick.h" +! +! char kernel_version[] = UTS_RELEASE; + + static struct JS_DATA_SAVE_TYPE JS_DATA[JS_MAX]; /* misc data */ + static int JS_EXIST; /* which joysticks' axis exist? */ +*************** +*** 42,53 **** +--- 51,66 ---- + /* get_timer0(): + returns the current value of timer 0. This is a 16 bit counter that starts + at LATCH and counts down to 0 */ ++ + inline int get_timer0 (void) + { + int t0, t1; + outb (0, PIT_MODE); + t0 = (int) inb (PIT_COUNTER_0); + t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0; ++ #ifdef CONFIG_PCSP ++ t1 = pcsp_clockticks - pcsp_timer0_latch + t1; ++ #endif + return (t1); + } + +*************** +*** 203,209 **** +--- 216,224 ---- + JS_DATA[0].JS_SAVE.buttons = ~(inb (JS_PORT) >> 4) & 0x03; + JS_DATA[1].JS_SAVE.buttons = (JS_DATA[0].JS_SAVE.buttons >> 2) & 0x03; + j = JS_DATA[minor].JS_TIMEOUT; ++ #ifndef CONFIG_PCSP + cli (); /*no interupts!*/ ++ #endif + outb (0xff, JS_PORT); /*trigger one-shots*/ + /*get init timestamp*/ + t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 (); +*************** +*** 218,224 **** +--- 233,241 ---- + if (chk & JS_Y_1) + t_y1 = get_timer0 (); + } ++ #ifndef CONFIG_PCSP + sti (); /*allow interupts*/ ++ #endif + JS_READ_SEMAPHORE = 0; /*allow other reads to progress*/ + if (j == 0) + return -ENODEV; /*read timed out*/ +*** Makefile.O Thu Mar 17 20:45:54 1994 +--- Makefile Thu Jan 12 13:09:03 1995 +*************** +*** 13,26 **** + jscal: jscal.c + $(CC) $(CFLAGS) jscal.c -O6 -DLINUX -o jscal + +! joystick.o: joystick.c release.h + $(CC) $(CFLAGS) joystick.c -c -O6 -D__KERNEL__ -DLINUX -o joystick.o + +- release.h: +- @echo char kernel_version[] = \"`uname -r`\"\; > release.h +- + clean: +! rm -f *.o $(PROGS) release.h *~ + + devs: + mknod /dev/js0 c 15 0 +--- 13,23 ---- + jscal: jscal.c + $(CC) $(CFLAGS) jscal.c -O6 -DLINUX -o jscal + +! joystick.o: joystick.c + $(CC) $(CFLAGS) joystick.c -c -O6 -D__KERNEL__ -DLINUX -o joystick.o + + clean: +! rm -f *.o $(PROGS) *~ + + devs: + mknod /dev/js0 c 15 0 diff -N -u -r linux-original/drivers/pcsnd/linux-1.2.0.pcsp.diff linux/drivers/pcsnd/linux-1.2.0.pcsp.diff --- linux-original/drivers/pcsnd/linux-1.2.0.pcsp.diff Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/linux-1.2.0.pcsp.diff Thu May 4 22:20:03 1995 @@ -0,0 +1,376 @@ +*** linux/init/main.c.orig Tue Mar 7 22:41:15 1995 +--- linux/init/main.c Wed Mar 8 21:27:41 1995 +*************** +*** 78,83 **** +--- 78,86 ---- + extern void sonycd535_setup(char *str, int *ints); + #endif CONFIG_CDU535 + void ramdisk_setup(char *str, int *ints); ++ #ifdef CONFIG_PCSP ++ extern void pcsp_setup(char *str, int *ints); ++ #endif + + #ifdef CONFIG_SYSVIPC + extern void ipc_init(void); +*************** +*** 206,211 **** +--- 209,217 ---- + #ifdef CONFIG_CDU31A + { "cdu31a=", cdu31a_setup }, + #endif CONFIG_CDU31A ++ #ifdef CONFIG_PCSP ++ { "pcsp=", pcsp_setup }, ++ #endif + { 0, 0 } + }; + +*** linux/kernel/sched.c.orig Wed Feb 1 20:46:08 1995 +--- linux/kernel/sched.c Wed Mar 8 21:27:41 1995 +*************** +*** 533,539 **** + * irq uses this to decide if it should update the user or system + * times. + */ +! static void do_timer(int irq, struct pt_regs * regs) + { + unsigned long mask; + struct timer_struct *tp; +--- 533,542 ---- + * irq uses this to decide if it should update the user or system + * times. + */ +! #ifndef CONFIG_PCSP +! static +! #endif +! void do_timer(int irq, struct pt_regs * regs) + { + unsigned long mask; + struct timer_struct *tp; +*** linux/kernel/time.c.orig Wed Feb 1 08:19:36 1995 +--- linux/kernel/time.c Wed Mar 8 21:27:41 1995 +*************** +*** 168,177 **** +--- 168,184 ---- + * + * If you are really that interested, you should be reading + * comp.protocols.time.ntp! ++ * ++ * Yes, here comes my first try to resolve this. MB ++ * + */ + + #define TICK_SIZE tick + ++ #ifdef CONFIG_PCSP ++ extern int pcsp_clockticks, pcsp_timer0_latch; ++ #endif ++ + static inline unsigned long do_gettimeoffset(void) + { + int count; +*************** +*** 181,186 **** +--- 188,196 ---- + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; ++ #ifdef CONFIG_PCSP ++ count = pcsp_clockticks - pcsp_timer0_latch + count; ++ #endif + /* we know probability of underflow is always MUCH less than 1% */ + if (count > (LATCH - LATCH/100)) { + /* check for pending timer interrupt */ +*** linux/kernel/ksyms.c.orig Tue Mar 7 22:41:15 1995 +--- linux/kernel/ksyms.c Wed Mar 8 21:27:41 1995 +*************** +*** 47,52 **** +--- 47,55 ---- + #if defined(CONFIG_MSDOS_FS) && !defined(CONFIG_UMSDOS_FS) + #include + #endif ++ #ifdef CONFIG_PCSP ++ #include ++ #endif + + #include + extern char floppy_track_buffer[]; +*************** +*** 356,361 **** +--- 359,369 ---- + X(msdos_unlink), + X(msdos_unlink_umsdos), + X(msdos_write_inode), ++ #endif ++ #ifdef CONFIG_PCSP ++ /* support for the joystick driver */ ++ X(pcsp_timer0_latch), ++ X(pcsp_clockticks), + #endif + /******************************************************** + * Do not add anything below this line, +*** linux/drivers/char/vt.c.orig Mon Mar 6 19:46:30 1995 +--- linux/drivers/char/vt.c Wed Mar 8 21:27:41 1995 +*************** +*** 7,12 **** +--- 7,13 ---- + * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 + */ + ++ #include + #include + #include + #include +*************** +*** 94,102 **** +--- 95,112 ---- + * We also return immediately, which is what was implied within the X + * comments - KDMKTONE doesn't put the process to sleep. + */ ++ ++ #ifdef CONFIG_PCSP ++ extern char pcsp_speaker; ++ #endif ++ + static void + kd_nosound(unsigned long ignored) + { ++ #ifdef CONFIG_PCSP ++ /* can't allow usage of counter 2 if /dev/pcsp use it */ ++ if (! pcsp_speaker) ++ #endif + /* disable counter 2 */ + outb(inb_p(0x61)&0xFC, 0x61); + return; +*************** +*** 107,112 **** +--- 117,126 ---- + { + static struct timer_list sound_timer = { NULL, NULL, 0, 0, kd_nosound }; + ++ #ifdef CONFIG_PCSP ++ /* can't allow usage of counter 2 if /dev/pcsp use it */ ++ if (! pcsp_speaker) { ++ #endif + cli(); + del_timer(&sound_timer); + if (count) { +*************** +*** 125,130 **** +--- 139,147 ---- + } else + kd_nosound(0); + sti(); ++ #ifdef CONFIG_PCSP ++ } ++ #endif + return; + } + +*** linux/drivers/char/mem.c.orig Wed Mar 8 21:25:10 1995 +--- linux/drivers/char/mem.c Wed Mar 8 21:27:41 1995 +*************** +*** 25,30 **** +--- 25,34 ---- + extern long soundcard_init(long mem_start); + #endif + ++ #ifdef CONFIG_PCSP ++ extern long pcsp_init(long mem_start); ++ #endif ++ + static int read_ram(struct inode * inode, struct file * file,char * buf, int count) + { + return -EIO; +*************** +*** 380,385 **** +--- 384,392 ---- + #endif + #ifdef CONFIG_SOUND + mem_start = soundcard_init(mem_start); ++ #endif ++ #ifdef CONFIG_PCSP ++ mem_start = pcsp_init(mem_start); + #endif + #if CONFIG_QIC02_TAPE + mem_start = qic02_tape_init(mem_start); +*** linux/drivers/Makefile.orig Sat Jan 7 11:57:43 1995 +--- linux/drivers/Makefile Wed Mar 8 21:27:41 1995 +*************** +*** 26,31 **** +--- 26,35 ---- + SUBDIRS := $(SUBDIRS) sound + endif + ++ ifdef CONFIG_PCSP ++ SUBDIRS := $(SUBDIRS) pcsnd ++ endif ++ + all: driversubdirs + + driversubdirs: dummy +*** linux/arch/i386/kernel/irq.c.orig Tue Mar 7 22:41:05 1995 +--- linux/arch/i386/kernel/irq.c Wed Mar 8 21:27:41 1995 +*************** +*** 23,28 **** +--- 23,29 ---- + #include + #include + #include ++ #include + + #include + #include +*************** +*** 364,366 **** +--- 365,464 ---- + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + } ++ ++ #ifdef CONFIG_PCSP ++ /* ++ * /dev/pcsp implementation, part of linux/arch/i386/kernel/irq.c ++ * ++ * Copyright (C) 1993, 1994 Michael Beck ++ */ ++ ++ static int (*pcsp_IRQ)(void) = NULL; ++ static const char *pcsp_irq0_owner = NULL; ++ ++ /* ++ * This is a mix of a fast and a slow interrupt; ++ * pcsp_IRQ is called like a fast int, if it returns a 0, ++ * the original (slow) handler is started ++ */ ++ extern void FAST_timer_irq(void); ++ __asm__( ++ "\n.align 4\n" ++ "_FAST_timer_irq:\n\t" ++ SAVE_MOST ++ ACK_FIRST(0x01) ++ "incl _intr_count\n\t" ++ "sti\n\t" ++ "call *_pcsp_IRQ\n\t" ++ "cmpl $0,%eax\n\t" ++ "je 9f\n\t" ++ "cli\n\t" ++ UNBLK_FIRST(0x01) ++ "decl _intr_count\n\t" ++ RESTORE_MOST ++ ++ "\n.align 4\n" ++ "9:\t" /* call the original _do_IRQ */ ++ "popl %ecx\n\t" ++ "popl %edx\n\t" ++ "popl %eax\n\t" ++ "pop %ds\n\t" ++ "pop %es\n\t" ++ ++ "pushl $-2\n\t" /* we know this is IRQ0 */ ++ SAVE_ALL ++ "movl %esp,%ebx\n\t" ++ "pushl %ebx\n\t" ++ "pushl $0\n\t" ++ "call _do_IRQ\n\t" ++ "addl $8,%esp\n\t" ++ "cli\n\t" ++ UNBLK_FIRST(0x01) ++ "decl _intr_count\n\t" ++ "jmp ret_from_sys_call\n" ++ ); ++ ++ /* ++ * Set the function func to be executed as fast-timer-int; ++ * if func returns a 0, the old IRQ0-handler is called ++ * This is only allowed if IRQ0 was a former SLOW-int; ++ * should be no problem because do_timer() use it normally :-) ++ */ ++ int pcsp_set_irq(int (*func)(void)) ++ { ++ unsigned long flags; ++ ++ pcsp_IRQ = func; ++ if (! pcsp_IRQ) ++ return -EINVAL; ++ if (! irq_action->name) ++ return -EINVAL; ++ if (irq_action->flags & SA_INTERRUPT) ++ return -EBUSY; ++ pcsp_irq0_owner = irq_action->name; ++ irq_action->name = "pcsp+timer"; ++ save_flags(flags); ++ cli(); ++ set_intr_gate(0x20, FAST_timer_irq); ++ restore_flags(flags); ++ return 0; ++ } ++ ++ /* ++ * reset the IRQ0 to default handling (a SLOW-int) ++ */ ++ int pcsp_release_irq(void) ++ { ++ unsigned long flags; ++ ++ if (! pcsp_IRQ) ++ return -EINVAL; ++ save_flags(flags); ++ cli(); ++ set_intr_gate(0x20, interrupt[0]); ++ restore_flags(flags); ++ pcsp_IRQ = NULL; ++ irq_action->name = pcsp_irq0_owner; ++ return 0; ++ } ++ #endif +*** linux/arch/i386/config.in.orig Mon Mar 6 19:46:29 1995 +--- linux/arch/i386/config.in Wed Mar 8 21:27:41 1995 +*************** +*** 254,259 **** +--- 254,260 ---- + comment 'Sound' + + bool 'Sound card support' CONFIG_SOUND n ++ bool 'PC-Speaker and DAC driver support' CONFIG_PCSP n + + comment 'Kernel hacking' + +*** linux/Makefile.orig Wed Mar 8 21:25:09 1995 +--- linux/Makefile Wed Mar 8 21:27:41 1995 +*************** +*** 100,105 **** +--- 100,109 ---- + DRIVERS := $(DRIVERS) drivers/sound/sound.a + endif + ++ ifdef CONFIG_PCSP ++ DRIVERS := $(DRIVERS) drivers/pcsnd/pcsp.a ++ endif ++ + include arch/$(ARCH)/Makefile + + .c.s: +*************** +*** 232,237 **** +--- 236,242 ---- + mrproper: clean + rm -f include/linux/autoconf.h include/linux/version.h + rm -f drivers/sound/local.h ++ rm -f drivers/pcsnd/local.h + rm -f .version .config* config.in config.old + rm -f include/asm + rm -f .depend `find . -name .depend -print` +*** linux/Configure.orig Thu Dec 29 18:58:40 1994 +--- linux/Configure Wed Mar 8 21:27:41 1995 +*************** +*** 129,134 **** +--- 129,137 ---- + if [ "$CONFIG_SOUND" = "y" ] ; then + $MAKE -C drivers/sound config || exit 1 + fi ++ if [ "$CONFIG_PCSP" = "y" ] ; then ++ $MAKE -C drivers/pcsnd config || exit 1 ++ fi + + rm -f .config.old + if [ -f .config ]; then diff -N -u -r linux-original/drivers/pcsnd/linux-1.2.8.pcsp.diff linux/drivers/pcsnd/linux-1.2.8.pcsp.diff --- linux-original/drivers/pcsnd/linux-1.2.8.pcsp.diff Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/linux-1.2.8.pcsp.diff Thu May 4 23:22:40 1995 @@ -0,0 +1,376 @@ +*** linux/init/main.c.orig Fri Feb 24 15:41:01 1995 +--- linux/init/main.c Thu May 4 20:22:32 1995 +*************** +*** 78,83 **** +--- 78,86 ---- + extern void sonycd535_setup(char *str, int *ints); + #endif CONFIG_CDU535 + void ramdisk_setup(char *str, int *ints); ++ #ifdef CONFIG_PCSP ++ extern void pcsp_setup(char *str, int *ints); ++ #endif + + #ifdef CONFIG_SYSVIPC + extern void ipc_init(void); +*************** +*** 206,211 **** +--- 209,217 ---- + #ifdef CONFIG_CDU31A + { "cdu31a=", cdu31a_setup }, + #endif CONFIG_CDU31A ++ #ifdef CONFIG_PCSP ++ { "pcsp=", pcsp_setup }, ++ #endif + { 0, 0 } + }; + +*** linux/kernel/sched.c.orig Wed Apr 12 20:24:17 1995 +--- linux/kernel/sched.c Thu May 4 20:22:32 1995 +*************** +*** 531,537 **** + * irq uses this to decide if it should update the user or system + * times. + */ +! static void do_timer(int irq, struct pt_regs * regs) + { + unsigned long mask; + struct timer_struct *tp; +--- 531,540 ---- + * irq uses this to decide if it should update the user or system + * times. + */ +! #ifndef CONFIG_PCSP +! static +! #endif +! void do_timer(int irq, struct pt_regs * regs) + { + unsigned long mask; + struct timer_struct *tp; +*** linux/kernel/time.c.orig Sun Mar 26 14:48:40 1995 +--- linux/kernel/time.c Thu May 4 20:22:32 1995 +*************** +*** 180,189 **** +--- 180,196 ---- + * + * If you are really that interested, you should be reading + * comp.protocols.time.ntp! ++ * ++ * Yes, here comes my first try to resolve this. MB ++ * + */ + + #define TICK_SIZE tick + ++ #ifdef CONFIG_PCSP ++ extern int pcsp_clockticks, pcsp_timer0_latch; ++ #endif ++ + static inline unsigned long do_gettimeoffset(void) + { + int count; +*************** +*** 193,198 **** +--- 200,208 ---- + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; ++ #ifdef CONFIG_PCSP ++ count = pcsp_clockticks - pcsp_timer0_latch + count; ++ #endif + /* we know probability of underflow is always MUCH less than 1% */ + if (count > (LATCH - LATCH/100)) { + /* check for pending timer interrupt */ +*** linux/kernel/ksyms.c.orig Tue May 2 07:05:05 1995 +--- linux/kernel/ksyms.c Thu May 4 20:22:32 1995 +*************** +*** 53,58 **** +--- 53,61 ---- + #if defined(CONFIG_MSDOS_FS) && !defined(CONFIG_UMSDOS_FS) + #include + #endif ++ #ifdef CONFIG_PCSP ++ #include ++ #endif + + #include + extern char floppy_track_buffer[]; +*************** +*** 382,387 **** +--- 385,395 ---- + X(msdos_unlink), + X(msdos_unlink_umsdos), + X(msdos_write_inode), ++ #endif ++ #ifdef CONFIG_PCSP ++ /* support for the joystick driver */ ++ X(pcsp_timer0_latch), ++ X(pcsp_clockticks), + #endif + /******************************************************** + * Do not add anything below this line, +*** linux/drivers/char/vt.c.orig Wed Feb 15 09:50:55 1995 +--- linux/drivers/char/vt.c Thu May 4 20:22:32 1995 +*************** +*** 7,12 **** +--- 7,13 ---- + * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 + */ + ++ #include + #include + #include + #include +*************** +*** 94,102 **** +--- 95,112 ---- + * We also return immediately, which is what was implied within the X + * comments - KDMKTONE doesn't put the process to sleep. + */ ++ ++ #ifdef CONFIG_PCSP ++ extern char pcsp_speaker; ++ #endif ++ + static void + kd_nosound(unsigned long ignored) + { ++ #ifdef CONFIG_PCSP ++ /* can't allow usage of counter 2 if /dev/pcsp use it */ ++ if (! pcsp_speaker) ++ #endif + /* disable counter 2 */ + outb(inb_p(0x61)&0xFC, 0x61); + return; +*************** +*** 107,112 **** +--- 117,126 ---- + { + static struct timer_list sound_timer = { NULL, NULL, 0, 0, kd_nosound }; + ++ #ifdef CONFIG_PCSP ++ /* can't allow usage of counter 2 if /dev/pcsp use it */ ++ if (! pcsp_speaker) { ++ #endif + cli(); + del_timer(&sound_timer); + if (count) { +*************** +*** 125,130 **** +--- 139,147 ---- + } else + kd_nosound(0); + sti(); ++ #ifdef CONFIG_PCSP ++ } ++ #endif + return; + } + +*** linux/drivers/char/mem.c.orig Tue Mar 7 13:59:28 1995 +--- linux/drivers/char/mem.c Thu May 4 20:22:32 1995 +*************** +*** 25,30 **** +--- 25,34 ---- + extern long soundcard_init(long mem_start); + #endif + ++ #ifdef CONFIG_PCSP ++ extern long pcsp_init(long mem_start); ++ #endif ++ + static int read_ram(struct inode * inode, struct file * file,char * buf, int count) + { + return -EIO; +*************** +*** 380,385 **** +--- 384,392 ---- + #endif + #ifdef CONFIG_SOUND + mem_start = soundcard_init(mem_start); ++ #endif ++ #ifdef CONFIG_PCSP ++ mem_start = pcsp_init(mem_start); + #endif + #if CONFIG_QIC02_TAPE + mem_start = qic02_tape_init(mem_start); +*** linux/drivers/Makefile.orig Sat Jan 7 11:57:43 1995 +--- linux/drivers/Makefile Thu May 4 20:22:32 1995 +*************** +*** 26,31 **** +--- 26,35 ---- + SUBDIRS := $(SUBDIRS) sound + endif + ++ ifdef CONFIG_PCSP ++ SUBDIRS := $(SUBDIRS) pcsnd ++ endif ++ + all: driversubdirs + + driversubdirs: dummy +*** linux/arch/i386/kernel/irq.c.orig Thu Mar 2 07:39:22 1995 +--- linux/arch/i386/kernel/irq.c Thu May 4 20:22:32 1995 +*************** +*** 23,28 **** +--- 23,29 ---- + #include + #include + #include ++ #include + + #include + #include +*************** +*** 364,366 **** +--- 365,464 ---- + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + } ++ ++ #ifdef CONFIG_PCSP ++ /* ++ * /dev/pcsp implementation, part of linux/arch/i386/kernel/irq.c ++ * ++ * Copyright (C) 1993, 1994 Michael Beck ++ */ ++ ++ static int (*pcsp_IRQ)(void) = NULL; ++ static const char *pcsp_irq0_owner = NULL; ++ ++ /* ++ * This is a mix of a fast and a slow interrupt; ++ * pcsp_IRQ is called like a fast int, if it returns a 0, ++ * the original (slow) handler is started ++ */ ++ extern void FAST_timer_irq(void); ++ __asm__( ++ "\n.align 4\n" ++ "_FAST_timer_irq:\n\t" ++ SAVE_MOST ++ ACK_FIRST(0x01) ++ "incl _intr_count\n\t" ++ "sti\n\t" ++ "call *_pcsp_IRQ\n\t" ++ "cmpl $0,%eax\n\t" ++ "je 9f\n\t" ++ "cli\n\t" ++ UNBLK_FIRST(0x01) ++ "decl _intr_count\n\t" ++ RESTORE_MOST ++ ++ "\n.align 4\n" ++ "9:\t" /* call the original _do_IRQ */ ++ "popl %ecx\n\t" ++ "popl %edx\n\t" ++ "popl %eax\n\t" ++ "pop %ds\n\t" ++ "pop %es\n\t" ++ ++ "pushl $-2\n\t" /* we know this is IRQ0 */ ++ SAVE_ALL ++ "movl %esp,%ebx\n\t" ++ "pushl %ebx\n\t" ++ "pushl $0\n\t" ++ "call _do_IRQ\n\t" ++ "addl $8,%esp\n\t" ++ "cli\n\t" ++ UNBLK_FIRST(0x01) ++ "decl _intr_count\n\t" ++ "jmp ret_from_sys_call\n" ++ ); ++ ++ /* ++ * Set the function func to be executed as fast-timer-int; ++ * if func returns a 0, the old IRQ0-handler is called ++ * This is only allowed if IRQ0 was a former SLOW-int; ++ * should be no problem because do_timer() use it normally :-) ++ */ ++ int pcsp_set_irq(int (*func)(void)) ++ { ++ unsigned long flags; ++ ++ pcsp_IRQ = func; ++ if (! pcsp_IRQ) ++ return -EINVAL; ++ if (! irq_action->name) ++ return -EINVAL; ++ if (irq_action->flags & SA_INTERRUPT) ++ return -EBUSY; ++ pcsp_irq0_owner = irq_action->name; ++ irq_action->name = "pcsp+timer"; ++ save_flags(flags); ++ cli(); ++ set_intr_gate(0x20, FAST_timer_irq); ++ restore_flags(flags); ++ return 0; ++ } ++ ++ /* ++ * reset the IRQ0 to default handling (a SLOW-int) ++ */ ++ int pcsp_release_irq(void) ++ { ++ unsigned long flags; ++ ++ if (! pcsp_IRQ) ++ return -EINVAL; ++ save_flags(flags); ++ cli(); ++ set_intr_gate(0x20, interrupt[0]); ++ restore_flags(flags); ++ pcsp_IRQ = NULL; ++ irq_action->name = pcsp_irq0_owner; ++ return 0; ++ } ++ #endif +*** linux/arch/i386/config.in.orig Fri Apr 28 10:17:56 1995 +--- linux/arch/i386/config.in Thu May 4 20:22:32 1995 +*************** +*** 258,263 **** +--- 258,264 ---- + comment 'Sound' + + bool 'Sound card support' CONFIG_SOUND n ++ bool 'PC-Speaker and DAC driver support' CONFIG_PCSP n + + comment 'Kernel hacking' + +*** linux/Makefile.orig Tue May 2 07:18:15 1995 +--- linux/Makefile Thu May 4 20:22:32 1995 +*************** +*** 100,105 **** +--- 100,109 ---- + DRIVERS := $(DRIVERS) drivers/sound/sound.a + endif + ++ ifdef CONFIG_PCSP ++ DRIVERS := $(DRIVERS) drivers/pcsnd/pcsp.a ++ endif ++ + include arch/$(ARCH)/Makefile + + .c.s: +*************** +*** 232,237 **** +--- 236,242 ---- + mrproper: clean + rm -f include/linux/autoconf.h include/linux/version.h + rm -f drivers/sound/local.h ++ rm -f drivers/pcsnd/local.h + rm -f .version .config* config.in config.old + rm -f include/asm + rm -f .depend `find . -name .depend -print` +*** linux/Configure.orig Thu Dec 29 18:58:40 1994 +--- linux/Configure Thu May 4 20:22:32 1995 +*************** +*** 129,134 **** +--- 129,137 ---- + if [ "$CONFIG_SOUND" = "y" ] ; then + $MAKE -C drivers/sound config || exit 1 + fi ++ if [ "$CONFIG_PCSP" = "y" ] ; then ++ $MAKE -C drivers/pcsnd config || exit 1 ++ fi + + rm -f .config.old + if [ -f .config ]; then diff -N -u -r linux-original/drivers/pcsnd/pcsndriv.c linux/drivers/pcsnd/pcsndriv.c --- linux-original/drivers/pcsnd/pcsndriv.c Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/pcsndriv.c Wed Jan 3 22:38:52 1996 @@ -0,0 +1,1189 @@ +/* + * linux/kernel/chr_drv/sound/pcsndriv.c + * + * /dev/pcsp implementation + * + * Copyright (C) 1993-1995 Michael Beck + */ + +/* + * FIXME!!!! + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tables.h" +#include "local.h" + +/* the timer stuff */ +#define TIMER_IRQ 0 + +/* the maximal samplerange for PC-Speaker: 18357 Hz */ +#define MIN_CONST 65 + +#define MAX_SRATE (CLOCK_TICK_RATE / MIN_CONST) +#define SRATE (CLOCK_TICK_RATE / pcsp.realrate) + +/* + * the default blocksize for playing thru + * PC-Speaker, STO1 and STNC use now 32k + */ + +#ifndef ABLK_SIZE +#define ABLK_SIZE 16184 +#endif + +/* the default samplerate for /dev/audio */ +#ifndef DEFAULT_RATE +#define DEFAULT_RATE 8000 +#endif + +#ifndef PCSP_MIXER +#define DEFAULT_LEFT 100 +#define DEFAULT_RIGHT 100 +#endif + +/* + * This function will be used to generate a time delay on pentium + * processors; this delay is only needed for the STO1 because it has + * only one latch, so we must wait some time after we write the value + * for the left channel before we can write the right one + * + * as long as I haven't such, it's empty :-) + */ + +#define PCSP_PENTIUM_DELAY + +/* + the "critical" frequency: if the machine is too slow for this, PCSP + is disabled +*/ +#ifndef PCSP_CRITICAL_FREQ +#define PCSP_CRITICAL_FREQ 12500 +#endif + +/* PCSP internal maximum volume, it's hardcoded */ +#define PCSP_MAX_VOLUME 256 + +/* need this macros */ +#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) +#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) + +/* the parallel-ports */ +static pcsp_ports[] = { 0x3bc, 0x378, 0x278 }; +static pcsp_port_enable[] = { 0, 0, 0 }; + +#define LP_NO 3 +#define LP_B(port) pcsp_ports[port] /* IO address */ +#define LP_S(port) inb_p(LP_B(port) + 1) /* status */ + +/* general pcsp data */ + +static struct pcsp_status { + int last_clocks; + unsigned char *buf[2]; /* double buffering */ + unsigned char *buffer; + unsigned char *end; + unsigned in[2]; /* buffers fill */ + unsigned xfer; + unsigned index; + unsigned volume; /* volume for pc-speaker */ + unsigned left; /* left volume */ + unsigned right; /* right volume */ + unsigned srate; /* sample rate */ + unsigned si; /* precalculated step const */ + unsigned timerC; /* hardware timer ticks for srate */ + unsigned timerCF; /* for fixed samplerate */ + unsigned act_dev; /* which device is playing */ + unsigned port; /* on which lp-port */ + unsigned portS; /* for Stereo */ + unsigned actual; /* actual buffer */ + unsigned realrate; /* the real sample rate */ + unsigned maxrate; /* maximum real sample rate */ + unsigned audio_fmt; /* 16 or 8 bit */ + unsigned fmt_msk; /* supported data formats */ + unsigned ablk_size; /* length of one audio-buffer */ + unsigned char e; + char timer_on; + char mode; /* Mono / Stereo */ + char is_ulaw; /* need ULAW->LINEAR */ +#ifdef PCSP_16BIT_SUPPORT + char stereo_emu; /* set if Stereo is emulated */ + char enable_emu; /* set if the emulation is enabled */ +#endif +} pcsp; + +/* Volume-tables */ + +static unsigned char vl_tab[256]; +static unsigned char left_vol[256], right_vol[256]; +#ifdef PCSP_16BIT_SUPPORT +static unsigned char left_volS[256], right_volS[256]; +#endif + +#ifdef PCSP_MIXER +extern int pcsp_mixer_set(int whichDev, unsigned int level); +extern int pcsp_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); +#endif + +/* need this to be global */ +char pcsp_active = 0; +char pcsp_speaker = 0; +char pcsp_enabled = -1; +volatile int pcsp_timer0_latch, pcsp_clockticks; + +static struct wait_queue *pcsp_sleep = NULL; + +extern int pcsp_set_irq(int (*func)(void)); +extern int pcsp_release_irq(void); +static void pcsp_stop_timer(void); + +/* test if a parallel port is free */ + +inline static int pcsp_free_port(int port) +{ + return (pcsp_port_enable[port] || !check_region(LP_B(port), 3)); +} + +static void pcsp_reserve_port(int port) +{ + pcsp_port_enable[port] = 1; + request_region(LP_B(port), 3, "pcsp"); +} + +/* test if a Stereo-on-One is on lp(port) */ +inline static int stereo1_detect(unsigned port) +{ + if (! pcsp_free_port(port)) + return 0; + outb(0x7F, LP_B(port)); + if (LP_S(port) & 0x80) { + outb(0x80, LP_B(port)); + return (LP_S(port) & 0x80) ? 0 : 1; + } + return 0; +} + +/* test if a new Stereo-Circuit is on lp(port) */ +inline static int stereo_nc_detect(unsigned port) +{ + if (! pcsp_free_port(port)) + return 0; + outb(0, LP_B(port)); + if (LP_S(port) & 0x40) { + outb(0xFF, LP_B(port)); + return (LP_S(port) & 0x40) ? 0 : 1; + } + return 0; +} + +/* search for Stereo-on-One, return it's port if found */ +static int stereo1_init(void) +{ + register int i; + + for (i = 0; i < LP_NO; ++i) + if (stereo1_detect(i)) { + pcsp_reserve_port(i); + pcsp.port = LP_B(i); + pcsp.act_dev = SNDCARD_STO1; + return i; + } + return (-ENODEV); +} + +/* search for Stereo-NC, return it's port if found */ +static int stereo_nc_init(void) +{ + register int i; + + for (i = 0; i < LP_NO; ++i) + if (stereo_nc_detect(i)) { + pcsp_reserve_port(i); + pcsp.port = LP_B(i); + pcsp.act_dev = SNDCARD_STNC; + return i; + } + return (-ENODEV); +} + +#ifdef PCSP_TEST_SPEED + +/* + this is a stupid beep which occurs if PCSP is disabled; + it's not needed because we have the message, but who reads it... + and this is the PC-Speaker driver :-) +*/ +void pcsp_beep(int count, int cycles) +{ + /* enable counter 2 */ + outb_p(inb_p(0x61)|3, 0x61); + /* set command for counter 2, 2 byte write */ + outb_p(0xB6, 0x43); + /* select desired HZ */ + outb_p(count & 0xff, 0x42); + outb((count >> 8) & 0xff, 0x42); + + while (cycles--); + + /* disable counter 2 */ + outb(inb_p(0x61)&0xFC, 0x61); +} + +volatile int pcsp_test_running; + +/* + the timer-int for testing cpu-speed, mostly the same as + for PC-Speaker + */ +static int pcsp_test_intspeed(void) +{ + if (pcsp.index < pcsp.in[pcsp.actual]) { + outb(pcsp.e, 0x61); + __asm__("xorb $0,%0\n\t" + "outb %0,$97" + : : "a" ((char)pcsp.e) ); + __asm__("xlatb\n\t" + "outb %0,$66" + : : "a" ((char)pcsp.buffer[pcsp.index]), + "b" ((long)vl_tab) ); + pcsp.xfer += pcsp.si; + pcsp.index = pcsp.xfer >> 16; + } + if (pcsp.index >= pcsp.in[pcsp.actual]) { + pcsp.xfer = pcsp.index = 0; + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + if (pcsp_sleep) /* NEVER */ + nop(); + if (pcsp.in[pcsp.actual] == 0xFFFF) + pcsp.actual ^= 1; + } + + if ( (pcsp_clockticks -= pcsp.timerCF) < 0) + pcsp_clockticks += LATCH; + ++pcsp_test_running; + return 1; +} + +/* + this routine measures the time needed for one timer-int if + we play thru PC-Speaker. This is kind of ugly but does the + trick. + */ +static int pcsp_measurement(int addon) +{ + int count; + unsigned long flags; + + pcsp_clockticks = 0; + pcsp.timerCF = LATCH; +/* + * FIXME!!!!!! What should be buffer size here?? [BT] + */ + pcsp.buf[0] = (unsigned char *)kmalloc( 127 * 1024, GFP_KERNEL ); + pcsp.buffer = pcsp.buf[0]; + pcsp.index = 0; + pcsp.xfer = 0; + pcsp.si = 1 << 16; + pcsp.in[0] = 5 + addon; + pcsp.in[1] = 0; + pcsp.actual = 0; + pcsp.e = inb(0x61) & 0xFC; + + pcsp_test_running = 0; + + if (pcsp_set_irq(pcsp_test_intspeed) < 0) + panic("PCSP could not modify timer IRQ!"); + + /* + Currently (0.99.15d) Linux call chr_dev_init with ints + disabled; so we need a sti() to enable them. + However, because this can be changed in the future we use + save_flags() and restore_flags() + */ + save_flags(flags); + sti(); + + /* + Perhaps we need some sort of timeout here, but if IRQ0 + isn't working the system hangs later ... + */ + while (pcsp_test_running < 5); + restore_flags(flags); + + if (pcsp_release_irq() < 0) + panic("PCSP could not reset timer IRQ!"); + + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + + kfree( pcsp.buf[0] ); + + return (LATCH - count); +} + +static int pcsp_test_speed(void) +{ + int worst, worst1, best, best1; + + worst = pcsp_measurement(0); + worst1 = pcsp_measurement(0); + best = pcsp_measurement(5); + best1 = pcsp_measurement(5); + + worst = MIN(worst, worst1); + best = MIN(best, best1); + +#ifdef PCSP_DEBUG + printk(" PCSP-Timerint needs %d Ticks in worst case\n", worst); + printk(" PCSP-Timerint needs %d Ticks in best case\n", best); +#endif + /* We allow a CPU-usage of 90 % for the best-case ! */ + pcsp.realrate = best * 10 / 9; + pcsp.maxrate = CLOCK_TICK_RATE / pcsp.realrate; + printk(" PCSP-Measurement: Maximal samplerate %d Hz", pcsp.maxrate); + + if (pcsp.maxrate > PCSP_CRITICAL_FREQ) { + if (MIN_CONST > pcsp.realrate) { + pcsp.realrate = MIN_CONST; + printk(", %d Hz", MAX_SRATE); + } + printk(" used\n"); + return 1; + } + + /* very ugly beep, but you hopefully never hear it */ + pcsp_beep(12000,800000); + pcsp_beep(10000,800000); + + printk("\n This is too SLOW! PCSP-driver DISABLED\n"); + return 0; +} +#endif + +/* the timer-int for playing thru PC-Speaker */ +int pcsp_do_timer(void) +{ + if (pcsp.index < pcsp.in[pcsp.actual]) { + outb(pcsp.e, 0x61); + __asm__("xorb $1,%0\n\t" + "outb %0,$97" + : : "a" ((char)pcsp.e) ); + __asm__("xlatb\n\t" + "outb %0,$66" + : : "a" ((char)pcsp.buffer[pcsp.index]), + "b" ((long)vl_tab) ); + pcsp.xfer += pcsp.si; + pcsp.index = pcsp.xfer >> 16; + } + if (pcsp.index >= pcsp.in[pcsp.actual]) { + pcsp.xfer = pcsp.index = 0; + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + if (pcsp_sleep) + wake_up_interruptible(&pcsp_sleep); + if (pcsp.in[pcsp.actual] == 0) + pcsp_stop_timer(); + } + + if ( (pcsp_clockticks -= pcsp.timerCF) < 0) { + pcsp_clockticks += LATCH; + return 0; + } + return 1; +} + +/* timer-int for playing thru STO1 */ +int pcsp_do_sto1_timer(void) +{ + static int ret; + + if (pcsp.buffer < pcsp.end) { + if (pcsp.mode) { + register short w = ((short)pcsp.port); + __asm__("xlatb\n\t" + "outb %0,%1\n\t" + "addl $2,%1\n\t" + "movb $1,%0\n\t" + "outb %0,%1\n\t" + "movb $0,%0\n\t" + "outb %0,%1" + : + : "a" ((char) *pcsp.buffer++), "d" ((short)w), "b" ((long)right_vol) + : "ax", "dx", "bx" + ); + + /* + * I move the following code because + * I need some time delay for the left DAC + * on my 486DX2 + * this will hopefully enough or we need some + * really time wasting jumps here + * + * This time delay will really be a problem for + * pentiums :-( + * + */ + + if ( (pcsp_clockticks -= pcsp.timerC) < 0) { + pcsp_clockticks += LATCH; + ret = 0; + } + else + ret = 1; + + PCSP_PENTIUM_DELAY; + + __asm__( "xlatb\n\t" + "outb %0,%1\n\t" + "addl $2,%1\n\t" + "movb $2,%0\n\t" + "outb %0,%1\n\t" + "movb $0,%0\n\t" + "outb %0,%1" + : + : "a" ((char) *pcsp.buffer++), "d" ((short)w), "b" ((long)left_vol) + : "ax", "dx", "bx" + ); + } + else { /* Mono */ + outb(left_vol[*pcsp.buffer++], (short)pcsp.port); + if ( (pcsp_clockticks -= pcsp.timerC) < 0) { + pcsp_clockticks += LATCH; + ret = 0; + } + else + ret = 1; + } + } + if (pcsp.buffer >= pcsp.end) { + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + pcsp.end = pcsp.buffer + pcsp.in[pcsp.actual]; + if (pcsp_sleep) + wake_up_interruptible(&pcsp_sleep); + if (pcsp.in[pcsp.actual] == 0) + pcsp_stop_timer(); + } + return (ret); +} + +/* timer-int for playing thru DACs */ +int pcsp_do_dac_timer(void) +{ + if (pcsp.buffer < pcsp.end) { + if (pcsp.act_dev == SNDCARD_DACS) { + if (pcsp.mode) { + outb(left_vol[*pcsp.buffer++], pcsp.port); + outb(right_vol[*pcsp.buffer++], pcsp.portS); + } + else { + outb(left_vol[*pcsp.buffer], pcsp.port); + outb(left_vol[*pcsp.buffer++], pcsp.portS); + } + } + else /* Simple DAC */ + outb(left_vol[*pcsp.buffer++], pcsp.port); + } + if (pcsp.buffer >= pcsp.end) { + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + pcsp.end = pcsp.buffer + pcsp.in[pcsp.actual]; + if (pcsp_sleep) + wake_up_interruptible(&pcsp_sleep); + if (pcsp.in[pcsp.actual] == 0) + pcsp_stop_timer(); + } + + if ( (pcsp_clockticks -= pcsp.timerC) < 0) { + pcsp_clockticks += LATCH; + return 0; + } + return 1; +} + +/* calculate all needed time-consts, return the 'adjusted' samplerate */ +static unsigned pcsp_calc_srate(unsigned rate) +{ + pcsp.timerC = (CLOCK_TICK_RATE + rate / 2) / rate; + pcsp.srate = (CLOCK_TICK_RATE + pcsp.timerC / 2) / pcsp.timerC; + /* and now for the PC-Speaker */ + + pcsp.timerCF = pcsp.realrate; + pcsp.si = (pcsp.srate << 16) / SRATE; + return pcsp.srate; +} + +static void pcsp_start_timer(void) +{ + int result; + + /* use the first buffer */ + pcsp.actual = 0; + pcsp.xfer = pcsp.index = 0; + pcsp.buffer = pcsp.buf[pcsp.actual]; + pcsp.end = pcsp.buffer + pcsp.in[pcsp.actual]; + + if (pcsp.act_dev == SNDCARD_PCSP) { + pcsp_speaker = 1; + pcsp.e = inb(0x61) | 0x03; + outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(pcsp.timerCF & 0xFF, 0x40); + outb(pcsp.timerCF >> 8 , 0x40); + if (pcsp_set_irq(pcsp_do_timer) < 0) + panic("PCSP: could not modify timer IRQ!"); + pcsp_timer0_latch = pcsp.timerCF; + } + else { /* it's a DAC */ + if (pcsp.act_dev == SNDCARD_STO1) + outb(3,pcsp.port + 2); + + /* get the timer */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(pcsp.timerC & 0xFF, 0x40); + outb(pcsp.timerC >> 8 , 0x40); + if (pcsp.act_dev == SNDCARD_STO1) + result = pcsp_set_irq(pcsp_do_sto1_timer); + else + result = pcsp_set_irq(pcsp_do_dac_timer); + if (result < 0) + panic("PCSP: could not modify timer IRQ!"); + pcsp_timer0_latch = pcsp.timerC; + } + pcsp_clockticks = pcsp.last_clocks; + pcsp.timer_on = 1; +} + +/* reset the timer to 100 Hz and reset old timer-int */ +static void pcsp_stop_timer(void) +{ + if (pcsp.timer_on) { + /* restore the timer */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + + /* clear clock tick counter */ + pcsp.last_clocks = pcsp_clockticks; + pcsp_timer0_latch = pcsp_clockticks = LATCH; + + if (pcsp_release_irq() < 0) + panic("PCSP: could not reset timer IRQ!"); + + pcsp.timer_on = 0; + } + + /* reset the buffer */ + pcsp.in[0] = pcsp.in[1] = 0; + pcsp.xfer = pcsp.index = 0; + pcsp.actual = 0; + pcsp.buffer = pcsp.end = pcsp.buf[pcsp.actual]; + + pcsp_speaker = 0; +} + +/* + calculate a translation-table for PC-Speaker +*/ +static void pcsp_calc_vol(int volume) +{ + int i, j; + + if (pcsp.is_ulaw) + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + if (j < -128) + j = -128; + if (j > 127) + j = 127; + vl_tab[i] = sp_tab[ulaw[j + 128]]; + } + else + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + if (j < -128) + j = -128; + if (j > 127) + j = 127; + vl_tab[i] = sp_tab[j + 128]; + } +} + +/* calculate linear translation table for DACs */ +static void pcsp_calc_voltab(int volume, unsigned char *tab) +{ + int i, j; + + if (pcsp.is_ulaw) + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + *tab++ = ulaw[j + 128]; + } + else + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + *tab++ = j + 128; + } +} +#ifdef PCSP_MIXER + +inline void pcsp_set_voltables(void) +{ +#ifdef PCSP_16BIT_SUPPORT + if (pcsp.stereo_emu) { + pcsp_calc_voltab(pcsp.left, left_volS); + pcsp_calc_voltab(pcsp.right, right_volS); + pcsp_calc_vol(PCSP_MAX_VOLUME); + pcsp_calc_voltab(PCSP_MAX_VOLUME, left_vol); + pcsp_calc_voltab(PCSP_MAX_VOLUME, right_vol); + } + else +#endif + { + pcsp_calc_voltab(pcsp.left, left_vol); + pcsp_calc_voltab(pcsp.right, right_vol); + pcsp_calc_vol(pcsp.volume); + } +} + +/* this is called if /dev/pcmixer change Mastervolume */ +inline void pcsp_set_volume(unsigned short v) +{ + pcsp.left = (((unsigned)(v & 0x7F) << 8) + 50) / 100; + pcsp.right = (((unsigned)(v & 0x7F00)) + 50) / 100; + pcsp.volume = (pcsp.right + pcsp.left) >> 1; + pcsp_set_voltables(); +} + +inline unsigned pcsp_get_mode(void) +{ + return pcsp.mode; +} +#endif + +/* + set the speed for /dev/pcsp, it's now from 4000 - 44100 Hz, + but DAC's are bounded by the maximal samplerate + */ +inline unsigned long pcsp_set_speed(unsigned long speed) +{ + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (pcsp.act_dev != SNDCARD_PCSP) + if (speed > pcsp.maxrate) + speed = pcsp.maxrate; + return speed; +} + +/* + set the audio type +*/ +int pcsp_set_format(int fmt) +{ + static char ulaw; + + if (fmt != AFMT_QUERY) { + ulaw = 0; + if (! (pcsp.fmt_msk & fmt)) /* Not supported */ + if (fmt == AFMT_MU_LAW) + ulaw = 1; + else + fmt = AFMT_U8; /* this is supported */ + pcsp.audio_fmt = fmt; + /* + we must recalculate the volume-tables is we change + ulaw-state + */ + if (pcsp.is_ulaw != ulaw) { + pcsp.is_ulaw = ulaw; + pcsp_set_voltables(); + } + } + return pcsp.audio_fmt; +} + +static void pcsp_sync(void) +{ + while (!(current->signal & ~current->blocked) && + (pcsp.in[0] || pcsp.in[1]) ) { + /* Wait until a complete block are ready */ + interruptible_sleep_on(&pcsp_sleep); + } +} + +static void pcsp_release(struct inode * inode, struct file * file) +{ + pcsp_sync(); + pcsp_stop_timer(); + outb_p(0xb6,0x43); /* binary, mode 2, LSB/MSB, ch 2 */ + + pcsp_active = 0; +} + +static int pcsp_open(struct inode * inode, struct file * file) +{ + if (pcsp_active) + return -EBUSY; + + pcsp.buffer = pcsp.end = pcsp.buf[0]; + pcsp.in[0] = pcsp.in[1] = 0; + pcsp.timer_on = 0; + + /* we set 8000 Hz for /dev/audio (ulaw flag set before open) */ + if (pcsp.is_ulaw) + pcsp_calc_srate(DEFAULT_RATE); + + pcsp_active = 1; + return 0; +} + +static int pcsp_ioctl_out(unsigned long *ptr, long value) +{ + static int error; + + if (value < 0) + return value; + error = verify_area(VERIFY_WRITE, ptr, sizeof(long)); + if (error) + return error; + put_fs_long(value, ptr); + return 0; +} + + +/* the new version 2 IOCTL's */ +static int pcsp_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + unsigned long ret; + unsigned long *ptr = (unsigned long *)arg; + int i, error; + +#ifdef PCSP_MIXER + if (((cmd >> 8) & 0xff) == 'M') /* it's a Mixer IOCTL */ + return pcsp_mixer_ioctl(inode, file, cmd, arg); +#endif + switch (cmd) { + case SNDCTL_DSP_SPEED: /* simulate SB 1.0 */ + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = pcsp_set_speed(get_fs_long(ptr)); + arg = pcsp_calc_srate(arg); + return pcsp_ioctl_out(ptr, arg); + case SNDCTL_DSP_STEREO: /* it's now possible */ + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = get_fs_long(ptr); + if (pcsp.act_dev == SNDCARD_STO1 || + pcsp.act_dev == SNDCARD_DACS ) +#ifdef PCSP_16BIT_SUPPORT + pcsp.stereo_emu = 0; + else { + pcsp.stereo_emu = (arg) ? 1 : 0; + /* test if the emulation is disabled */ + if (! pcsp.enable_emu && pcsp.stereo_emu) { + pcsp.stereo_emu = 0; + pcsp.mode = 0; + return -EINVAL; + } + } + pcsp.mode = (arg) ? 1 : 0; + pcsp_set_voltables(); +#else + pcsp.mode = (arg) ? 1 : 0; + else { + pcsp.mode = 0; + if (arg) + return -EINVAL; + } +#endif + return (0); + case SNDCTL_DSP_GETBLKSIZE: + return pcsp_ioctl_out(ptr, pcsp.ablk_size); + case SNDCTL_DSP_SYNC: /* syncinc, so speed changes work correct */ + pcsp_sync(); + pcsp_stop_timer(); + return (0); + case SNDCTL_DSP_RESET: /* stops output immediately */ + pcsp_stop_timer(); + pcsp_calc_srate(DEFAULT_RATE); + return (0); + case SNDCTL_DSP_GETFMTS: + return pcsp_ioctl_out(ptr, pcsp.fmt_msk); + case SNDCTL_DSP_SETFMT: + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = get_fs_long(ptr); + return pcsp_ioctl_out(ptr, pcsp_set_format(arg)); + + /* + the following ioctls currently do nothing, but + exist for compatibility; however, because pcsp's + implementation is somewhat strange, they are not needed, + because pcsp start output after any count of data + written to one buffer + this may change in the future + */ + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + case SNDCTL_DSP_POST: + return (0); + + case PCSP_SET_DEV: + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = get_fs_long(ptr); + switch(arg) { + case SNDCARD_STO1: + if (stereo1_init() < 0) + return (-ENODEV); + break; + case SNDCARD_PCSP: + case SNDCARD_DACM: + case SNDCARD_DACS: + pcsp.act_dev = arg; break; + case SNDCARD_STNC: + if (stereo_nc_init() < 0) + return (-ENODEV); + break; + default: + return (-ENODEV); + } + /* Perhaps we need to adjust the samplerate */ + pcsp.srate = pcsp_set_speed(pcsp.srate); + pcsp_calc_srate(pcsp.srate); + return (0); + case PCSP_GET_DEV: + return pcsp_ioctl_out(ptr, pcsp.act_dev); + case PCSP_SET_PORTS: + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = get_fs_long(ptr); + if ((arg & 0xFF) < LP_NO && (arg >> 8) < LP_NO) { + if (pcsp.act_dev == SNDCARD_STO1) { + if (stereo1_detect(arg & 0xFF)) { + pcsp.port = LP_B(arg & 0xFF); + return (0); + } + } + else if (pcsp.act_dev == SNDCARD_STNC) { + if (stereo_nc_detect(arg & 0xFF)) { + pcsp.port = LP_B(arg & 0xFF); + return (0); + } + } + else { + pcsp.port = LP_B(arg & 0xFF); + pcsp.portS = LP_B(arg >> 8); + return (0); + } + } + return (-EINVAL); + case PCSP_GET_PORTS: + ret = 0; + for (i = 0; i < LP_NO; ++i) + if (LP_B(i) == pcsp.port) + ret = i; + for (i = 0; i < LP_NO; ++i) + if (LP_B(i) == pcsp.portS) + ret |= i << 8; + return pcsp_ioctl_out(ptr, ret); + case PCSP_GET_VOL: + return pcsp_ioctl_out(ptr, pcsp.volume); + case PCSP_SET_VOL: + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + pcsp.volume = get_fs_long(ptr); +#ifdef PCSP_MIXER + arg = MIN(256, pcsp.volume); + arg = (arg * 100) / 256; + arg = (arg << 8 ) | arg; + pcsp_mixer_set(SOUND_MIXER_VOLUME, arg); +#else + pcsp_calc_vol(pcsp.volume); +#endif + return (0); + case PCSP_GET_SRATE: + return pcsp_ioctl_out(ptr, SRATE); + case PCSP_SET_SRATE: + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = get_fs_long(ptr); + if (arg < 10000 || arg > MAX_SRATE || arg > pcsp.maxrate) + return (-EINVAL); + pcsp.realrate = (CLOCK_TICK_RATE + arg / 2) / arg; + return (0); + case PCSP_GET_MEASURE: + return pcsp_ioctl_out(ptr, pcsp.maxrate); + case PCSP_SET_EMU_MODE: +#ifdef PCSP_16BIT_SUPPORT + error = verify_area(VERIFY_READ, ptr, 4); + if (error) + return (error); + arg = get_fs_long(ptr); + if (arg == PCSP_EMULATION_ON) { + pcsp.enable_emu = 1; + pcsp.fmt_msk |= AFMT_S16_LE; + } + else if (arg == PCSP_EMULATION_OFF) { + pcsp.enable_emu = 0; + pcsp.fmt_msk &= ~AFMT_S16_LE; + } + return pcsp_ioctl_out(ptr, pcsp.enable_emu); +#endif + default : + return (-EINVAL); + } +} + +static int pcsp_read(struct inode * inode, struct file * file, char * buffer, int count) +{ + return -EINVAL; +} + +static int pcsp_write(struct inode * inode, struct file * file, + char * buffer, int count) +{ + unsigned long copy_size; + unsigned long total_bytes_written = 0; + unsigned bytes_written; + int i; +#ifdef PCSP_16BIT_SUPPORT + int factor = 1; + +#define CNT(x) ((x) * factor) +#else +#define CNT(x) (x) +#endif + +#ifdef PCSP_16BIT_SUPPORT + /* the audio format shouldn't be changed during output */ + if (pcsp.audio_fmt == AFMT_S16_LE) { + count >>= 1; + factor <<= 1; + } + if (pcsp.stereo_emu) { + count >>= 1; + factor <<= 1; + } +#endif + do { + bytes_written = 0; + copy_size = (count <= pcsp.ablk_size) ? count : pcsp.ablk_size; + i = pcsp.in[0] ? 1 : 0; + if (copy_size && !pcsp.in[i]) { + +#ifdef PCSP_16BIT_SUPPORT + if (pcsp.audio_fmt == AFMT_S16_LE) + if (pcsp.stereo_emu) + __asm__( + "cld\n" + "1:\t" + "incl %%esi\n\t" + "fs ; movb (%%esi),%%al\n\t" + "xorb $0x80,%%al\n\t" + "xlat\n\t" + "incl %%esi\n\t" + "xchgl %%ebx, %%edx\n\t" + "movb %%al,%%ah\n\t" + "incl %%esi\n\t" + "fs ; movb (%%esi),%%al\n\t" + "xorb $0x80,%%al\n\t" + "xlat\n\t" + "incl %%esi\n\t" + "xchgl %%ebx, %%edx\n\t" + "addb %%ah,%%al\n\t" + "rcrb $1,%%al\n\t" + "stosb\n\t" + "loop 1b" + : /* no outputs */ + :"b" ((long) left_volS), + "d" ((long) right_volS), + "c" (copy_size), + "D" ((long) pcsp.buf[i]), + "S" ((long) buffer) + :"ax","bx","cx","dx", + "si","di","memory"); + else + __asm__( + "cld\n" + "1:\t" + "incl %%esi\n\t" + "fs ; lodsb\n\t" + "xorb $0x80,%%al\n\t" + "stosb\n\t" + "loop 1b" + : /* no outputs */ + :"c" (copy_size), + "D" ((long) pcsp.buf[i]), + "S" ((long) buffer) + :"ax","cx","di","si","memory"); + else + if (pcsp.stereo_emu) + __asm__( + "cld\n" + "1:\t" + "fs ; lodsw\n\t" + "xlat\n\t" + "xchgl %%edx,%%ebx\n\t" + "xchgb %%ah,%%al\n\t" + "xlat\n\t" + "xchgl %%edx,%%ebx\n\t" + "addb %%ah,%%al\n\t" + "rcrb $1,%%al\n\t" + "stosb\n\t" + "loop 1b" + : /* no outputs */ + :"b" ((long) left_volS), + "d" ((long) right_volS), + "c" (copy_size), + "D" ((long) pcsp.buf[i]), + "S" ((long) buffer) + :"ax","bx","cx","dx", + "si","di","memory"); + else +#endif + memcpy_fromfs(pcsp.buf[i], buffer, copy_size); + pcsp.in[i] = copy_size; + if (! pcsp.timer_on) + pcsp_start_timer(); + bytes_written += copy_size; + buffer += copy_size; + } + + if (pcsp.in[0] && pcsp.in[1]) { + interruptible_sleep_on(&pcsp_sleep); + if (current->signal & ~current->blocked) { + if (total_bytes_written + bytes_written) + return CNT(total_bytes_written + bytes_written); + else + return -EINTR; + } + } + total_bytes_written += bytes_written; + count -= bytes_written; + + } while (count > 0); + return CNT(total_bytes_written); +#undef CNT +} + +void pcsp_setup(char *s, int *p) +{ + if (!strcmp(s, "off")) { + pcsp_enabled = 0; + return; + } + if (p[0] > 0) + pcsp.maxrate = p[1]; + pcsp_enabled = 1; +} + +struct file_operations pcspeaker_pcsp_fops = { + NULL, /* pcsp_seek */ + pcsp_read, + pcsp_write, + NULL, /* pcsp_readdir */ + NULL, /* pcsp_select */ + pcsp_ioctl, /* pcsp_ioctl */ + NULL, /* pcsp_mmap */ + pcsp_open, + pcsp_release, +}; + +void pcspeaker_init(void) +{ + int i; + + if (pcsp_enabled < 0) { +#ifdef PCSP_TEST_SPEED + pcsp_enabled = pcsp_test_speed(); +#else + pcsp.maxrate = 44100; /* only a BIG freq */ + pcsp.realrate = (CLOCK_TICK_RATE + SELECTED_SRATE / 2) / + SELECTED_SRATE; + pcsp_enabled = 1; +#endif + } + else { + pcsp.realrate = MIN(pcsp.maxrate, MAX_SRATE); + pcsp.realrate = (CLOCK_TICK_RATE + pcsp.realrate / 2) / + pcsp.realrate; + } + if (! pcsp_enabled) + return; + + pcsp.xfer = 0; + pcsp_clockticks = pcsp_timer0_latch = pcsp.last_clocks = LATCH; + pcsp_active = 0; + pcsp_speaker = 0; + pcsp.timer_on = 0; + pcsp.mode = 0; + pcsp.is_ulaw = 0; + pcsp.audio_fmt = AFMT_U8; +#ifdef PCSP_16BIT_SUPPORT + pcsp.fmt_msk = AFMT_U8 | AFMT_S16_LE; +#else + pcsp.fmt_msk = AFMT_U8; +#endif + pcsp.buffer = pcsp.buf[0]; + pcsp.in[0] = pcsp.in[1] = 0; + pcsp.actual = 0; + pcsp.act_dev = SNDCARD_PCSP; + pcsp.port = pcsp.portS = 0; + pcsp.volume = (DEFAULT_LEFT + DEFAULT_RIGHT) * 128 / 100; + pcsp.ablk_size = ABLK_SIZE; + pcsp_calc_srate(DEFAULT_RATE); + pcsp_calc_vol(pcsp.volume); + pcsp_calc_voltab(DEFAULT_LEFT, left_vol); + pcsp_calc_voltab(DEFAULT_RIGHT, right_vol); +#ifdef PCSP_16BIT_SUPPORT + memcpy(left_volS, left_vol, sizeof(left_volS)); + memcpy(right_volS, right_vol, sizeof(right_volS)); + pcsp.stereo_emu = 0; + pcsp.enable_emu = 1; +#endif + printk(" PC-Speaker"); + i = stereo1_init(); + if (i >= 0) { + printk(", Stereo-on-One at lpt%d", i); + pcsp.ablk_size = 2 * ABLK_SIZE; + } + i = stereo_nc_init(); + if (i >= 0) { + printk(", New Stereo Circuit at lpt%d", i); + pcsp.ablk_size = 2 * ABLK_SIZE; + } + pcsp.buf[0] = (unsigned char *) kmalloc( 2 * pcsp.ablk_size, GFP_KERNEL ); + pcsp.buf[1] = pcsp.buf[0] + pcsp.ablk_size; +} diff -N -u -r linux-original/drivers/pcsnd/pcsp.c linux/drivers/pcsnd/pcsp.c --- linux-original/drivers/pcsnd/pcsp.c Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/pcsp.c Wed Jan 3 22:05:13 1996 @@ -0,0 +1,90 @@ +/* + * linux/kernel/chr_drv/sound/pcsp.c + * + * /dev/pcsp implementation + * + * Copyright (C) 1993, 1994 Michael Beck + */ + +#include + +#include +#include +#include +#include + +#include "local.h" + +extern struct file_operations pcspeaker_pcsp_fops, pcsp_mixer_fops; +extern void pcspeaker_init(void); +extern void pcsp_mixer_init(void); +extern long pcsp_set_format(long); +extern char pcsp_enabled; + +static int pcsp_open_dev(struct inode * inode, struct file * file) +{ + int minor = MINOR(inode->i_rdev); + + if (! pcsp_enabled) + return -ENODEV; + + file->f_op = &pcspeaker_pcsp_fops; + switch (minor) { + case PCSP_DSP_MINOR: + pcsp_set_format(AFMT_U8); + break; + case PCSP_AUD_MINOR: + pcsp_set_format(AFMT_MU_LAW); /* input is ULAW */ + break; +#ifdef PCSP_16BIT_SUPPORT + case PCSP_DSP16_MINOR: + if (pcsp_set_format(AFMT_S16_LE) != AFMT_S16_LE) + return -ENODEV; + break; +#endif +#ifdef PCSP_MIXER + case PCSP_MIXER_MINOR: + file->f_op = &pcsp_mixer_fops; + break; +#endif + default: + return -ENODEV; + } + return file->f_op->open(inode,file); +} + +static struct file_operations pcsp_fops = { + NULL, /* seek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + pcsp_open_dev, + NULL /* release */ +}; + +void pcsp_init(void) +{ + /* if disabled in commandline */ + if (! pcsp_enabled) { + printk("PCSP-device disabled\n"); + return; + } + + if (register_chrdev(PCSP_MAJOR, "pcsp", &pcsp_fops)) + printk("unable to get major %d for pcsp devices\n", PCSP_MAJOR); + else { + printk("PCSP-device 0.9 init:\n"); + pcspeaker_init(); +#ifdef PCSP_MIXER + if (pcsp_enabled) { + pcsp_mixer_init(); + printk(", PCSP-Mixer"); + } +#endif + } + if (pcsp_enabled) + printk(" installed\n"); +} diff -N -u -r linux-original/drivers/pcsnd/pcsp_mixer.c linux/drivers/pcsnd/pcsp_mixer.c --- linux-original/drivers/pcsnd/pcsp_mixer.c Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/pcsp_mixer.c Wed Jan 3 20:45:39 1996 @@ -0,0 +1,245 @@ +/* + * linux/kernel/chr_drv/pcsp/pcsp_mixer.c + * + * /dev/pcsp implementation - simple Mixer routines + * + * (C) 1993, 1994 Michael Beck + * Craig Metz (cmetz@thor.tjhsst.edu) + */ + +#include "local.h" + +#ifdef PCSP_MIXER + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define IOCTL_FROM_USER(d, s, o, c) memcpy_fromfs((d), &((s)[o]), (c)) +#define IOCTL_TO_USER(d, o, s, c) memcpy_tofs(&((d)[o]), (s), (c)) +#define IOCTL_IN(arg) get_fs_long((long *)(arg)) +#define IOCTL_OUT(arg, ret) (put_fs_long(ret, (long *)arg), 0) +#define OFF 0 + +static int rec_devices = (0); /* No recording source */ + +static struct { + char active; +} pcmixer; + +#define POSSIBLE_RECORDING_DEVICES (0) +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_VOLUME) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + (DEFAULT_RIGHT << 8) + DEFAULT_LEFT, /* Master Volume */ + /* The next devices are not supported, so they are zero */ + 0, /* Bass */ + 0, /* Treble */ + 0, /* FM */ + 0, /* PCM */ + 0, /* PC Speaker */ + 0, /* Ext Line */ + 0, /* Mic */ + 0, /* CD */ + 0, /* Recording monitor */ + 0, /* SB PCM */ + 0 /* Recording level */ +}; + +extern void pcsp_set_volume(unsigned short); +extern unsigned pcsp_get_mode(void); + +int pcsp_mixer_set(int whichDev, unsigned int level) +{ + int left, right; + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* Master volume (0-127) */ + levels[whichDev] = left | (right << 8); + pcsp_set_volume(levels[whichDev]); + break; + + default: + return (-EINVAL); + } + + return (levels[whichDev]); +} + +static int mixer_set_levels(struct sb_mixer_levels *user_l) +{ +#define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15)) + + struct sb_mixer_levels l; + + IOCTL_FROM_USER((char *) &l, (char *) user_l, 0, sizeof (l)); + + if (l.master.l & ~0xF || l.master.r & ~0xF + || l.line.l & ~0xF || l.line.r & ~0xF + || l.voc.l & ~0xF || l.voc.r & ~0xF + || l.fm.l & ~0xF || l.fm.r & ~0xF + || l.cd.l & ~0xF || l.cd.r & ~0xF + || l.mic & ~0x7) + return (-EINVAL); + + /* We only set the Master-volume, so the mixer is degenerate */ + pcsp_mixer_set(SOUND_MIXER_VOLUME, cmix (l.master)); + return (0); +} + +/* Read the current mixer level settings into the user's struct. */ +static int mixer_get_levels(struct sb_mixer_levels *user_l) +{ + + struct sb_mixer_levels l; + + l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100; /* Master */ + l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100; /* Master */ + + l.line.r = 0; /* Line */ + l.line.l = 0; + + l.voc.r = 0; /* DAC */ + l.voc.l = 0; + + l.fm.r = 0; /* FM */ + l.fm.l = 0; + + l.cd.r = 0; /* CD */ + l.cd.l = 0; + + l.mic = 0; /* Microphone */ + + IOCTL_TO_USER((char *) user_l, 0, (char *) &l, sizeof (l)); + return (0); +} + +/* Read the current mixer parameters into the user's struct. */ +static int mixer_get_params(struct sb_mixer_params *user_params) +{ + struct sb_mixer_params params; + + params.record_source = SRC_MIC; + params.hifreq_filter = OFF; + params.filter_input = OFF; + params.filter_output = OFF; + params.dsp_stereo = pcsp_get_mode(); + + IOCTL_TO_USER((char *) user_params, 0, (char *) ¶ms, sizeof(params)); + return (0); +} + +int pcsp_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + + if (((cmd >> 8) & 0xff) == 'M') + { + if (cmd & IOC_IN) + return IOCTL_OUT(arg, pcsp_mixer_set(cmd & 0xff, IOCTL_IN (arg))); + else + { /* Read parameters */ + + switch (cmd & 0xff) + { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT(arg, rec_devices); + break; + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT(arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE)); + break; + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT(arg, SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT(arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES); + break; + + case SOUND_MIXER_CAPS: + case SOUND_MIXER_MUTE: + case SOUND_MIXER_ENHANCE: + case SOUND_MIXER_LOUD: + return IOCTL_OUT(arg, 0); + break; + + + default: + return IOCTL_OUT(arg, levels[cmd & 0xff]); + } + } + } + else + { + switch (cmd) + { + case MIXER_IOCTL_SET_LEVELS: + mixer_set_levels((struct sb_mixer_levels *) arg); + return mixer_get_levels((struct sb_mixer_levels *) arg); + case MIXER_IOCTL_SET_PARAMS: + return mixer_get_params((struct sb_mixer_params *) arg); + case MIXER_IOCTL_READ_LEVELS: + return mixer_get_levels((struct sb_mixer_levels *) arg); + case MIXER_IOCTL_READ_PARAMS: + return mixer_get_params((struct sb_mixer_params *) arg); + case MIXER_IOCTL_RESET: + pcsp_mixer_set(SOUND_MIXER_VOLUME, levels[SOUND_MIXER_VOLUME]); + return (0); + default: + return (-EINVAL); + } + } + return (-EINVAL); +} + +static void pcsp_mixer_release(struct inode * inode, struct file * file) +{ + --pcmixer.active; +} + +static int pcsp_mixer_open(struct inode * inode, struct file * file) +{ + if (pcmixer.active) + return (-EBUSY); + ++pcmixer.active; + return (0); +} + +static int pcsp_mixer_inval(struct inode * inode, struct file * file, char * buffer, int count) +{ + return (-EINVAL); +} + +struct file_operations pcsp_mixer_fops = { + NULL, /* pcsp_seek */ + pcsp_mixer_inval, + pcsp_mixer_inval, + NULL, /* pcsp_readdir */ + NULL, /* pcsp_select */ + pcsp_mixer_ioctl, + NULL, /* pcsp_mmap */ + pcsp_mixer_open, + pcsp_mixer_release, +}; + +void pcsp_mixer_init(void) +{ + pcsp_mixer_set(SOUND_MIXER_VOLUME, levels[SOUND_MIXER_VOLUME]); + pcmixer.active = 0; +} + +#endif diff -N -u -r linux-original/drivers/pcsnd/pcspinstall linux/drivers/pcsnd/pcspinstall --- linux-original/drivers/pcsnd/pcspinstall Thu Jan 1 03:00:00 1970 +++ linux/drivers/pcsnd/pcspinstall Fri May 5 19:50:53 1995 @@ -0,0 +1,189 @@ +#!/bin/sh +# +# pcspinstall (C) 1995 Michael Beck +# +# Create the devices +# +# PCSP mixer (13, 0) (simulate /dev/mixer) +# pcsp (13, 3) (simulate /dev/dsp) +# SPARC audio (13, 4) (simulate /dev/audio) +# +# and (just kidding, because the driver converts 16bit audio to 8 :-) +# +# pcsp16 (13, 5) (simulate /dev/dsp16) +# + +function major () { + ls -l $1 | gawk '{ print $5 }' | sed 's/,//' +} + +function remove () { + echo -n "Remove $1 and $ACTION ? " + read ANSWER + if [ "$ANSWER" = "y" -o "$ANSWER" = "Y" ]; then + if rm -f $1 ;then + echo ok. + return 0 + else + echo "Could not remove $1." + echo "Please log in as root and remove it, then restart $0." + exit 1 + fi + else + echo "Did not remove $1, but you may run into difficulties later." + fi + return 1 +} + +function testdev () { + if [ -e $1 ]; then + if [ ! -c $1 ]; then + ANSWER=N + else + MAJOR=`major $1` + if [ "$MAJOR" -ne 13 ]; then + ANSWER=N + fi + fi + else + ANSWER=N + fi +} + +function makedev () { + + if [ -e $1 ]; then + if [ ! -c $1 ]; then + echo "$1 exist but isn't a character device." + remove $1 && mknod -m 666 $1 c 13 $2 + else + MAJOR=`major $1` + if [ "$MAJOR" -ne 13 ]; then + if [ "$MAJOR" -ne 30 ]; then + echo "$1 exist but has the wrong MAJOR number $MAJOR." + else + echo "$1 exists but has the old MAJOR number $MAJOR." + fi + remove $1 && mknod -m 666 $1 c 13 $2 + fi + fi + else + mknod -m 666 $1 c 13 $2 + fi +} + +function makelnk () { + + if [ -e $1 ]; then + if [ -L $1 ]; then + LINK=`ls -l $1 | gawk '{ print $11 }'` + BASE=`basename $2` + if [ "$LINK" = "$BASE" -o "$LINK" = "$2" ] ; then + return 0 + else + echo "$1 is a symbolic link but points to $LINK instead to $2." + remove $1 && ln -sf $2 $1 + fi + else + if [ ! -c $1 ]; then + echo "$1 exist but isn't a character device or a symbolic link." + remove $1 && ln -sf $2 $1 + else + MAJOR=`major $1` + if [ "$MAJOR" -ne 13 ]; then + if [ "$MAJOR" -eq 14 ]; then + cat < and create one if not exist +# +if [ ! -e /usr/include/sys/pcsp.h ]; then + echo '#include ' > /usr/include/sys/pcsp.h +fi +# +# check for and create one if not exist +# +if [ ! -e /usr/include/sys/soundcard.h ]; then + echo "/usr/include/sys/soundcard.h didn't exist, create one." + echo '#include ' > /usr/include/sys/soundcard.h +fi + +# +# check for the right major number +# +ANSWER=Y +testdev /dev/pcmixer +testdev /dev/pcsp +testdev /dev/pcaudio +testdev /dev/pcsp16 +if [ "$ANSWER" = 'Y' ]; then + exit 0 +fi + +cat < + +/* MUST BE defined from 0.5 */ +#define SOUND_VERSION 0x90 /* read 0.90 */ + +#define PCSP_MAJOR 13 /* to avoid conficts with iBCS-2 */ +#define PCSP_MIXER_MINOR 0 +#define PCSP_DSP_MINOR 3 +#define PCSP_AUD_MINOR 4 +#define PCSP_DSP16_MINOR 5 + +/* card ID, real soundcards use 0 to ... */ + +#define SNDCARD_PCSP 128 +#define SNDCARD_STO1 129 +#define SNDCARD_DACM 130 +#define SNDCARD_DACS 131 +#define SNDCARD_STNC 132 + +/* IOCTL commands, compatible to real /dev/dsp */ +/* SAMPLESIZE returns only the value, doesn't support changes */ + +#define SNDCTL_DSP_RESET _IO ('P', 0) +#define SNDCTL_DSP_SYNC _IO ('P', 1) +#define SNDCTL_DSP_SPEED _IOWR('P', 2, int) +#define SNDCTL_DSP_STEREO _IOWR('P', 3, int) +#define SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) +#define SNDCTL_DSP_SAMPLESIZE SNDCTL_DSP_SETFMT +#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int) +#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int) +#define SNDCTL_DSP_POST _IO ('P', 8) +#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int) +#define SNDCTL_DSP_SETFRAGMENT _IOWR('P',10, int) + +/* Audio data formats (Note! U8=8 and S16_LE=16 for compatibility) */ +#define SNDCTL_DSP_GETFMTS _IOR ('P',11, int) /* Returns a mask */ +#define SNDCTL_DSP_SETFMT _IOWR('P',5, int) /* Selects ONE fmt*/ +# define AFMT_QUERY 0x00000000 /* Return current fmt */ +# define AFMT_MU_LAW 0x00000001 +# define AFMT_A_LAW 0x00000002 +# define AFMT_IMA_ADPCM 0x00000004 +# define AFMT_U8 0x00000008 +# define AFMT_S16_LE 0x00000010 /* Little endian signed 16*/ +# define AFMT_S16_BE 0x00000020 /* Big endian signed 16 */ +# define AFMT_S8 0x00000040 +# define AFMT_U16_LE 0x00000080 /* Little endian U16 */ +# define AFMT_U16_BE 0x00000100 /* Big endian U16 */ + +#define SOUND_PCM_READ_RATE _IOR ('P', 2, int) +#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int) +#define SOUND_PCM_READ_BITS _IOR ('P', 5, int) +#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int) + +/* Some alias names */ +#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SETFMT +#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED +#define SOUND_PCM_POST SNDCTL_DSP_POST +#define SOUND_PCM_RESET SNDCTL_DSP_RESET +#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC +#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE +#define SOUND_PCM_SETFRAGMENT SNDCTL_DSP_SETFRAGMENT +#define SOUND_PCM_GETFMTS SNDCTL_DSP_GETFMTS +#define SOUND_PCM_SETFMT SNDCTL_DSP_SETFMT + + +/* IOCTL for changing the play-device, real sample rate etc. */ + +#define PCSP_SET_DEV 0x00014350 +#define PCSP_GET_DEV 0x00024350 +#define PCSP_SET_PORTS 0x00034350 +#define PCSP_GET_PORTS 0x00044350 +#define PCSP_SET_VOL 0x00054350 +#define PCSP_GET_VOL 0x00064350 +#define PCSP_SET_SRATE 0x00074350 +#define PCSP_GET_SRATE 0x00084350 +#define PCSP_GET_MEASURE 0x00094350 +#define PCSP_SET_EMU_MODE 0x000A4350 + +#define PCSP_EMULATION_OFF 0 +#define PCSP_EMULATION_ON 1 +#define PCSP_EMULATION_QUERY 2 + +/********************************************* + * IOCTL commands for /dev/mixer + */ + +/* + * Mixer devices + * + * There can be up to 20 different analog mixer channels. The + * SOUND_MIXER_NRDEVICES gives the currently supported maximum. + * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells + * the devices supported by the particular mixer. + */ + +#define SOUND_MIXER_NRDEVICES 12 +#define SOUND_MIXER_VOLUME 0 +#define SOUND_MIXER_BASS 1 +#define SOUND_MIXER_TREBLE 2 +#define SOUND_MIXER_SYNTH 3 +#define SOUND_MIXER_PCM 4 +#define SOUND_MIXER_SPEAKER 5 +#define SOUND_MIXER_LINE 6 +#define SOUND_MIXER_MIC 7 +#define SOUND_MIXER_CD 8 +#define SOUND_MIXER_IMIX 9 /* Recording monitor */ +#define SOUND_MIXER_ALTPCM 10 +#define SOUND_MIXER_RECLEV 11 /* Recording level */ + +/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */ +/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */ +#define SOUND_ONOFF_MIN 28 +#define SOUND_ONOFF_MAX 30 +#define SOUND_MIXER_MUTE 28 /* 0 or 1 */ +#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */ +#define SOUND_MIXER_LOUD 30 /* 0 or 1 */ + +/* Note! Number 31 cannot be used since the sign bit is reserved */ + +#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ + "Mic ", "CD ", "Mix ", "Pcm2 ", "rec"} + +#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \ + "mic", "cd", "mix", "pcm2", "rec"} + +/* Device bitmask identifiers */ + +#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */ +#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */ +#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */ +#define SOUND_MIXER_CAPS 0xfc + #define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */ +#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */ + +/* Device mask bits */ + +#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME) +#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS) +#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE) +#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH) +#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM) +#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER) +#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE) +#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC) +#define SOUND_MASK_CD (1 << SOUND_MIXER_CD) +#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX) +#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM) +#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV) + +#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) +#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE) +#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD) + +#define MIXER_READ(dev) _IOR('M', dev, int) +#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS) +#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM) +#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE) +#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC) +#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD) +#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX) +#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE) +#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC) +#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK) +#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK) +#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS) +#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS) + +#define MIXER_WRITE(dev) _IOWR('M', dev, int) +#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME) +#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS) +#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE) +#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH) +#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM) +#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER) +#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE) +#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC) +#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD) +#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX) +#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM) +#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV) +#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE) +#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE) +#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD) + +#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC) + +/* + * The following mixer ioctl calls are compatible with the BSD driver by + * Steve Haehnichen + */ + +typedef unsigned char BYTE; +typedef unsigned char FLAG; +struct stereo_vol +{ + BYTE l; /* Left volume */ + BYTE r; /* Right volume */ +}; + +#define MIXER_IOCTL_SET_LEVELS _IOW ('s', 20, struct sb_mixer_levels) +#define MIXER_IOCTL_SET_PARAMS _IOW ('s', 21, struct sb_mixer_params) +#define MIXER_IOCTL_READ_LEVELS _IOR ('s', 22, struct sb_mixer_levels) +#define MIXER_IOCTL_READ_PARAMS _IOR ('s', 23, struct sb_mixer_params) +#define MIXER_IOCTL_RESET _IO ('s', 24) + +/* + * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL + */ +struct sb_mixer_levels +{ + struct stereo_vol master; /* Master volume */ + struct stereo_vol voc; /* DSP Voice volume */ + struct stereo_vol fm; /* FM volume */ + struct stereo_vol line; /* Line-in volume */ + struct stereo_vol cd; /* CD audio */ + BYTE mic; /* Microphone level */ +}; + +/* + * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS + */ +struct sb_mixer_params +{ + BYTE record_source; /* Recording source (See SRC_xxx below) */ + FLAG hifreq_filter; /* Filter frequency (hi/low) */ + FLAG filter_input; /* ANFI input filter */ + FLAG filter_output; /* DNFI output filter */ + FLAG dsp_stereo; /* 1 if DSP is in Stereo mode */ +}; + +#define SRC_MIC 1 /* Select Microphone recording source */ +#define SRC_CD 3 /* Select CD recording source */ +#define SRC_LINE 7 /* Use Line-in for recording source */ + + +#if defined(MODULE) || defined(__KERNEL__) + +/* + * for the joystick driver + */ +extern volatile int pcsp_timer0_latch, pcsp_clockticks; + +#endif +#endif diff -N -u -r linux-original/init/main.c linux/init/main.c --- linux-original/init/main.c Sun Jan 7 17:39:41 1996 +++ linux/init/main.c Fri Jan 5 06:54:16 1996 @@ -108,6 +108,10 @@ static void prompt_ramdisk(char *str, int *ints); #endif CONFIG_BLK_DEV_RAM +#ifdef CONFIG_PCSP +extern void pcsp_setup(char *str, int *ints); +#endif + #ifdef CONFIG_SYSVIPC extern void ipc_init(void); #endif @@ -248,6 +252,9 @@ #ifdef CONFIG_CDU31A { "cdu31a=", cdu31a_setup }, #endif CONFIG_CDU31A +#ifdef CONFIG_PCSP + { "pcsp=", pcsp_setup }, +#endif #ifdef CONFIG_MCD { "mcd=", mcd_setup }, #endif CONFIG_MCD diff -N -u -r linux-original/kernel/ksyms.c linux/kernel/ksyms.c --- linux-original/kernel/ksyms.c Sun Jan 7 17:41:06 1996 +++ linux/kernel/ksyms.c Sun Jan 7 10:29:53 1996 @@ -98,6 +98,9 @@ #if defined(CONFIG_MSDOS_FS) && !defined(CONFIG_UMSDOS_FS) #include #endif +#ifdef CONFIG_PCSP +#include +#endif #if defined(CONFIG_PROC_FS) #include @@ -559,6 +562,11 @@ X(msdos_unlink), X(msdos_unlink_umsdos), X(msdos_write_inode), +#endif +#ifdef CONFIG_PCSP + /* support for the joystick driver */ + X(pcsp_timer0_latch), + X(pcsp_clockticks), #endif #ifdef CONFIG_PROC_FS X(proc_register),