diff options
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | Changelog | 111 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | README | 149 | ||||
-rw-r--r-- | hardsid.c | 1840 | ||||
-rw-r--r-- | hardsid.h | 36 |
6 files changed, 2493 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +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) year 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. + + <signature of Ty Coon>, 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 --git a/Changelog b/Changelog new file mode 100644 index 0000000..fb7e86f --- /dev/null +++ b/Changelog @@ -0,0 +1,111 @@ +0.16 27.12.2003 + - added support for Catweasel MK3 card + - added support for PCI HardSID cards + - more modular for easier support of new card types + - updated kernel compatibility and cleanups + - updated Makefile for 2.6 kernels + +0.15a 26.1.2002 + - fixed read ioctl that Sidplay2 is going to use + +0.15 25.1.2002 + - added /proc support for 2.2 kernels. The file is not + /proc/driver/hardsid but /proc/hardsid as there is no /proc/driver + directory in 2.2 :( For consistency moved it in 2.4 kernels too. + - fixed a mute problem from the sidplay2 patch + - added ability to disable filters via ioctl + - reduced speaker pops and clicks by modifing reset ioctl + - fixed oops with multiple chips + - added flush, read and delay ioctls + - all configurable things are now normal module options + - writes cleaned up, only one function does it anymore + - ioctl definitions put to separate header file + - faked reads to write only registers speed up some tunes + - added Quattro hack to play a tune through all SIDs on a Quattro card + - compatibility with the first 2.5 kernels + +0.14 23.2.2001 + - CVS repository moved to Sourceforge + - updated sidplay2 patch, unpacked + - added devfs support + - added card detection override hack + - added mute support + +0.13 27.1.2001 + - SID type autodetection tested to work with 8580 + - DUH! 0.12 oopses if you try to open an unexisting device... + - slightly updated sidplay2 patch + +0.12 27.1.2001 + - fixed a problem with 2.2 kernels. It seems doing a sema_init on + semaphore doesn't initialize it properly, but doing a init_MUTEX before + it does... grr... + - added newer sidplay2 patch, check README for build instructions + +0.11 25.1.2001 + - tested to work on Alpha! Required compiler options are documented in + Makefile + - multiple SID support! Now you can use 1-16 SIDs at the same time + using /dev/sidXX where each device maps to one SID. This has been + tested to work with hacked Sidplay2 using stereo .MUS files and two + normal HardSID cards (on Alpha :) + - device numbers changed for multisid support (60 by default, change + from source), minors are from 0 (/dev/sid0) to 15 (/dev/sid15). I + applied for an assigned major for this device (60 is marked as for + local/experimental use), let's see how it goes. + - fixed a bug with reads and Quattro cards + - simplified reset ioctl + +0.10 24.1.2001 + - moved to Sourceforge! http://hardsid.sourceforge.net, or + http://www.sourceforge.net/projects/hardsid, as the first URL + doesn't seem to work yet. + - some cleanup and initial support for Quattro cards (card type and all + chips should be detected, only first one is used for now though). + - added ioctl to query card type + +0.09 15.1.2001 + - Added support for dummy writes (register 0x1f) for delays longer than + 0xffff cycles, fixed David Whittaker's Lazy Jones. + +0.08 14.1.2001 + - Added CVS tags + - Fixed reset, the previous version actually didn't work... + +0.07 14.1.2001 + - #include cleanup + - some ioctls (reset, fifo status, SID type) + +0.06 5.1.2001 + - Added autodetection of the HardSID cards including checking the chip + type (6581/8580). Both are of course untested except with my 6581 at + 0x300 :) Success/failure reports very welcome, especially from 8580 + users. The code detects all four possible cards, but uses only the + first one for now. + - SID reading now actually works, you have to OR 0x20 to the register + when writing it to the chip before reading. + +0.05 3.1.2001 + - Ouch, reads froze the machine, now fixed. They still are quite + problematic as the whole buffer has to be flushed before reading. + +0.04 31.12.2000 + - All kinds of statistics + - Some timing changes + +0.03 18.12.2000 + - SMP support now tested to work, some small locking changes + +0.02 16.12.2000 + - Added some SMP locks + - Tested to work on 2.2.18 + - Always delay a bit (in this case two cycles) to give SID some time + to process writes + - Reset all registers on open, turn master volume on for some tunes + - Wait until all writes are done before reading + +0.01 15.12.2000 + - Totally untested support for 2.2 kernels + +0.0 15.12.2000 + - Initial release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..06e0e18 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the HardSID device driver. + +obj-m += hardsid.o + +KVER = $(shell uname -r) +KDIR = /lib/modules/$(KVER)/build + +# default:: kmod_build + +kmod_build:: $(fglrx-libs) $(fglrx-cfiles) $(fglrx-hdrs) + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +clean:: + rm -f *.o *.ko *.mod.* Module.symvers .hardsid* + rm -rf .tmp_versions + @@ -0,0 +1,149 @@ +------------------------------------------------------------------------------ +This version of the HARDSID driver is a small patch to the version available +at Sourceforge. I've not made any serious code changes -- just enough to make +it compile with my kernel (2.6.20-16-generic in Ubuntiu 7.04). + +The original README for the patched version is below. + +Ian C <ianc@noddybox.co.uk> +------------------------------------------------------------------------------ + + +HardSID device driver for Linux +Version 0.16 + +Copyright (c) 2000-2003 Jarno Paananen <jpaana@s2.org> +Copyright (c) 2001-2003 Simon White <s_a_white@email.com> + +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version +* 2 of the License, or (at your option) any later version. + + +This is a small hackish device driver for the HardSID and HardSID Quattro +cards (both ISA and PCI) by Hard Software and Catweasel MK3 card by +Individual Computers. + +WARNING: before you read any further, please bear in mind that this is very +experimental software. It could blow you and your machine to moon and back +at any time. It hasn't done that to me though :) + +This version has been tested to work on 2.4.23 and 2.6.0 kernels It might or +might not work for you, patches to get it working are happily accepted. SMP +has been tested to work on 2.4.0-test12 and 2.2.18. It has also been tested +on Alpha using kernel 2.4.0-ac10. Check the Makefile for necessary compiler +options for Alpha. The driver has also been tested to work with 64-bit AMD64 +kernels, both 2.4 and 2.6. + +NOTE! +----- +If you are using 2.2.x kernel and SMP, you have to add -D__SMP__ to the +command line in Makefile, otherwise your machine will lock up. + +If you want to use 2.2.x kernel, it seems your need at least 2.2.18, earlier +kernels don't have the 2.4 compatibility macros I'm using here. + +Due to the changes made to Makefiles between 2.4 and 2.6 kernels, I +separated the them to two different files. Copy/symlink appropriate to +Makefile before compiling. + +FOR OLD USERS +------------- +The device numbers have changed as of 0.11 version, read the information +below on how to remake your /dev entries. + + +Kernel driver +------------- + +Currently the driver uses a character device called /dev/sid, which should +be a symlink to sid0-15, with major number 60 and minor numbers 0-15. Each +device maps to one SID. + +So before you get to use the driver, you need to make the device(s): + +mknod /dev/sid0 c 60 0 +mknod /dev/sid1 c 60 1 +(and so on...) +ln -s /dev/sid0 /dev/sid + +Change the owner/group and permissions if you want non-root users to be able +to use it as well. + +The device driver should compile with just "make", but check that it gets +your kernel includes from the right place. + +At this point you should be ready for insmod hardsid.o. If this went well +and you got message like: + +HardSID Driver v0.16 +HardSID card with 6581 detected @ 0x300 + +in your dmesg, you should be set. + + +User mode +--------- + +Sidplay2 supports this driver in the CVS version now. You need to get and +build the Hardsid-builder. Use --hardsid switch with Sidplay2 to get it use +Hardsid. + +If you have two SIDs, you can try playing stereo SIDs too. + + +Miscellaneous +------------- + +- the module accepts a few options: + io=0x300: force the IO port base, not necessary + ioextent=8: how many ports to allocate, not necessary + major=60: the device major number to use, change if your + system already uses that. Also change the /dev-nodes. + slowaccess=1: use slow 8-bit access to the HardSID card if you have + problems with the default 16-bit. Shouldn't be necessary. + detecthack=1: force the driver to load even if it doesn't find any SIDs. + Useful only for debugging. + quattrohack=1: play the first SID data with all SIDs in a Quattro. My + personal hack, which might be useful to others too. + renice=-5: set the renice value of the kernel thread. Improves timing + when the machine is under heavy load. +- increasing the kernel scheduling rate (HZ) from <asm/param.h> decreases + CPU usage and timing errors of the driver +- there is also /proc/hardsid, which displays some statistics +- to write stuff to the sid, you write commands with 32 bits each to the + device: + bits 31-16: 16 bit delay timer value in C64 clock cycles + bits 15-13: reserved, keep zero + bits 12-8: SID register number + bits 7-0: Data + +This is in host byte order, ie. little endian in x86 case. + +Example: + + unsigned char reg, val; + unsigned int temp; + temp = ( cycles << 16 )| (reg << 8) | val; + write(sidHandle, &temp, 4); + +Multiple commands can be written at once. + +Register 0x1f is considered dummy (it doesn't exist in real SID) and is used +for delays longer than 0xffff. + + +- to read stuff, you seek to the correct register and read one byte: + + unsigned char reg, val; + lseek(sidHandle, reg, 0); + read(sidHandle, &val, 1); + +- there are a few ioctls you can use with the device, check the source for + those :) + +If you have any questions, patches or anything else in mind, feel free to +mail me at jpaana@s2.org + +// Jarno diff --git a/hardsid.c b/hardsid.c new file mode 100644 index 0000000..66c945f --- /dev/null +++ b/hardsid.c @@ -0,0 +1,1840 @@ +/* + * + * Linux driver for the HardSID cards by Hard Software + * and Catweasel MK3 by Individual Computers + * + * Copyright (C) 2000-2003 Jarno Paananen <jpaana@s2.org> + * Copyright (C) 2001-2003 Simon White <s_a_white@email.com> + * + * Based on the Linux kernel RTC driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +/* + * hardsid.c,v + * Revision 1.48 2003/12/27 19:27:17 jpaana + * 0.16 release + * + * Revision 1.47 2003/11/18 13:08:54 jpaana + * Fixes for devfs in 2.6 kernels + * + * Revision 1.46 2003/09/24 20:22:51 s_a_white + * Fixed assignment of pci hardsid card type to be after the sid structure + * is cleared. Prevent hardsid module causing a segmentation fault when + * loaded with no hardsid card available (don't call pci_unregister_driver + * if pci_module_init failed). + * + * Revision 1.45 2003/09/08 05:05:27 jpaana + * New minor handling for 2.6.0-test4 or so + * + * Revision 1.44 2003/04/17 17:41:09 jpaana + * Probe also other ISA ports than the first one + * + * Revision 1.43 2003/04/07 00:19:38 jpaana + * Some init and remove cleanups, fix for preemptible and later 2.5 kernels + * + * Revision 1.42 2003/03/13 22:35:47 s_a_white + * Make temp sid object used for probing global to prevent stack overflows + * (fixes crash on insmod). + * + * Revision 1.41 2003/03/13 10:06:43 s_a_white + * Add PCI hardsid support. Attempt to make different card types live + * together and support potential PNP hotswapping. Code currently takes + * the kernel out during insmod! + * + * Revision 1.40 2003/03/13 09:57:46 s_a_white + * Adjust peek/poke functions to be part of the sid object. This way we + * only need to work out the functions to use once, removing various + * switches/if's from the code. The peek/poke functions also have direct + * access to the sid objects data to support PCI hardsid differences. + * + * Revision 1.39 2003/02/10 05:23:20 jpaana + * First version of Catweasel MK3 support + * + * Revision 1.38 2002/07/28 17:54:35 jpaana + * 2.4.19 apparently uses the same renice mechanism as 2.5 + * + * Revision 1.37 2002/02/01 02:11:17 jpaana + * Another 2.5 kernel fix + * + * Revision 1.36 2002/01/30 05:28:26 jpaana + * reparent_to_init is not present nor needed in 2.2 apparently + * + * Revision 1.35 2002/01/30 05:00:37 jpaana + * reparent_to_init to avoid zombies + * + * Revision 1.34 2002/01/25 23:27:14 jpaana + * Fixed read ioctl, 0.15a release + * + * Revision 1.33 2002/01/25 04:49:35 jpaana + * 0.15 release + * + * Revision 1.32 2002/01/15 16:29:11 jpaana + * Another change for 2.5 kernels + * + * Revision 1.31 2002/01/07 13:48:39 jpaana + * Fixed to compile on newer 2.5.2-pre versions (check is for 2.5.1 as I use 2.5.1-dj series at the moment...) + * + * Revision 1.30 2001/11/09 01:35:52 jpaana + * Add renice value to module options + * + * Revision 1.29 2001/11/08 21:05:25 s_a_white + * File tidy. Fixed resid style faked reads to decay properly. + * Added modversions.h to remove unresolved symbols. + * + * Revision 1.28 2001/11/07 21:57:51 jpaana + * - reset all SIDs when closing and using the Quattro hack + * - merged Simon's changes including: + * - faked reads of write only registers + * (speeds up Fred Gray's tunes for example) + * - write clean up (writes to command buffer in one place only) + * - separated ioctl definitions to a header file for use with user land tools + * - added read and delay ioctls + * - fixed devfs (my bad...) + * + * Revision 1.27 2001/09/30 02:26:58 jpaana + * Changed config #defines to real module options + * + * Revision 1.26 2001/09/30 01:44:47 jpaana + * Added MODULE_LICENSE tag + * + * Revision 1.25 2001/09/04 20:01:24 jpaana + * - removed unnecessary #ifdefs from the devfs-support + * - added my Quattro hack + * - renice the play thread a bit as the realtime-stuff doesn't seem to work + * + * Revision 1.24 2001/08/23 01:36:16 jpaana + * Fix for kernels > 2.4.8 and some cosmetic stuff + * + * Revision 1.23 2001/03/31 11:45:20 jpaana + * Added flush ioctl + * + * Revision 1.22 2001/03/15 05:03:21 jpaana + * Fix oops with multiple chips + * + * Revision 1.21 2001/03/03 23:33:44 s_a_white + * Reduce speaker pops and clicks by modifing reset ioctl. + * + * Revision 1.20 2001/02/28 20:55:11 s_a_white + * Added ability to disable filters (swhite), usefull for debugging. /proc/hardsid + * now returns mute and filter states. + * + * Revision 1.19 2001/02/28 09:49:19 jpaana + * Remove a debug printk from mute ioctl + * + * Revision 1.18 2001/02/27 05:48:03 jpaana + * Added /proc support for 2.2 kernels + * + * Revision 1.17 2001/02/23 17:32:21 jpaana + * 0.14 release + * + * Revision 1.16 2001/02/11 17:07:38 jpaana + * Unused variables cleaned up + * + * Revision 1.15 2001/02/02 12:14:58 jpaana + * Added mute support + * + * Revision 1.14 2001/01/31 15:43:21 jpaana + * - DEVFS support + * - detection override hack for testing + * + * Revision 1.13 2001/01/27 06:11:03 jpaana + * 0.13 release + * + * Revision 1.12 2001/01/27 06:08:20 jpaana + * duh... fixed oops on trying to open unexisting device + * + * Revision 1.11 2001/01/26 22:38:45 jpaana + * 0.12 release + * - removed test hacks left from previous commit + * + * Revision 1.10 2001/01/26 22:15:01 jpaana + * Fixed 2.2 semaphore problem + * + * Revision 1.9 2001/01/25 03:38:26 jpaana + * - multiple SID support, major changes nearly everywhere + * - device number changed from misc devices to own major (60 for now) + * - tested to work on Alpha + * - fixed a bug with reads and Quattro cards + * - simplified reset ioctl + * + * Revision 1.8 2001/01/24 01:22:52 jpaana + * Forgot slow IO access on + * + * Revision 1.7 2001/01/24 01:19:24 jpaana + * - cleanup + * - initial HardSID Quattro support + * - added ioctl to query card type + * + * Revision 1.6 2001/01/14 23:32:09 jpaana + * 0.09 release + * + * Revision 1.5 2001/01/14 23:30:46 jpaana + * Printk cleanup and added support for dummy writes for delays longer than 0xffff + * + * Revision 1.4 2001/01/14 23:14:03 jpaana + * Fixed reset + * + * Revision 1.3 2001/01/14 22:50:49 jpaana + * Fixed RCSID + * + * Revision 1.2 2001/01/14 22:49:37 jpaana + * Added CVS tags + * + */ + + +#define HSID_VERSION "0.16" + +const char rcsid[] = "hardsid.c,v 1.48 2003/12/27 19:27:17 jpaana Exp"; + +#include <linux/version.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,10) +#include <linux/modversions.h> +#endif +/*#include <linux/config.h>*/ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/version.h> +#include <linux/delay.h> + +#include <linux/pci.h> + +#include <asm/io.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# define KERNEL_2_2 +# define __exit +#endif + +#define HSID_MAX_CARDS 4 +#define HSID_MAX_SIDS_PER_CARD 4 +#define HSID_MAX_SIDS (HSID_MAX_CARDS * HSID_MAX_SIDS_PER_CARD) + +/* DEVFS */ +#ifdef CONFIG_DEVFS_FS +#include <linux/devfs_fs_kernel.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) +static devfs_handle_t hsid_handles[HSID_MAX_SIDS]; +#endif +#endif + +#include "hardsid.h" + + +/* + All of these are module options, no need to hardcode them here unless your + kernel is ancient and doesn't support MODULE_PARM stuff + */ + +/* + If for some reason the autodetection fails, you can try fiddling with the + following variables, but that should not be necessary. + */ +static int io = 0x300; +static int ioextent = 8; +/* The major device number we use */ +static int major = 60; +/* If 16-bit access doesn't work for you for some reason, set this to 1 */ +static int slowaccess = 0; +/* Set this to 1 if you want to be able to load this module even + without a SID, used mainly for debugging */ +static int detecthack = 0; +/* Set this to 1 if you want to play the first SID stuff with all + chips in a Quattro. This is a major hack, but works for me(tm) */ +static int quattrohack = 0; +/* Set the kernel thread renice value */ +static int renice = -5; + +/* Nothing configurable found below */ + + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#define GETMINOR(file) (iminor((file)->f_dentry->d_inode)) +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +#define GETMINOR(file) (minor((file)->f_dentry->d_inode->i_rdev)) +#else +#define GETMINOR(file) (MINOR((file)->f_dentry->d_inode->i_rdev)) +#endif +#endif + +/* Bits in sid_d.status. */ +#define HSID_IS_OPEN 0x01 /* means /dev/sidX is in use */ +#define HSID_IS_REGISTERED 0x02 /* device registered */ + +/* These IDs are not registered and may belong to others */ +#define PCI_VENDOR_ID_INDIVIDUAL 0xe159 +#define PCI_DEVICE_ID_INDIVIDUAL_CWMK3 0x0001 +#define PCI_SUBSYSTEM_VENDOR_ID_INDIVIDUAL 0x1212 +#define PCI_SUBSYSTEM_DEVICE_ID_INDIVIDUAL_CWMK3 0x0002 +#define PCI_VENDOR_ID_HARDSOFTWARE 0x6581 +#define PCI_DEVICE_ID_HARDSOFTWARE_HSID 0x8580 + +static __initdata struct pci_device_id id_table[] = { + { PCI_VENDOR_ID_INDIVIDUAL, PCI_DEVICE_ID_INDIVIDUAL_CWMK3, + PCI_SUBSYSTEM_VENDOR_ID_INDIVIDUAL, + PCI_SUBSYSTEM_DEVICE_ID_INDIVIDUAL_CWMK3, 0, 0, 0 }, + /* @FIXME@ PCI_ANY_ID will work for now, probably best to + * insert the correct values here when known */ + { PCI_VENDOR_ID_HARDSOFTWARE, PCI_DEVICE_ID_HARDSOFTWARE_HSID, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, } +}; + + + +/* Sid type enum */ +typedef enum +{ + SID_NONE = 0, + SID_6581, + SID_8580 +} sid_type; + +/* Card type enum */ +typedef enum +{ + SID_CARD_NONE = 0, + SID_CARD_HARDSID, + SID_CARD_QUATTRO, + SID_CARD_CWMK3, + SID_CARD_PCI_HARDSID, + SID_CARD_PCI_QUATTRO +} sid_card_type; + +static const char * const sid_card[] = +{ + "", + "HardSID", + "HardSID Quattro", + "Catweasel MK3", + "HardSID PCI", + "HardSID PCI Quattro" +}; + + +/* Per sid card data */ +#define HSID_BUFFER_SIZE 8192 +typedef struct sid_d +{ + unsigned short port; + unsigned short port2; + unsigned char chip; + sid_type type; + sid_card_type card; + struct pci_dev* pcidev; + int curCommand; + int lastCommand; + struct semaphore bufferSem; + struct semaphore todoSem; + struct timeval tv; + int cycles; + int status; + __u32 buffer[HSID_BUFFER_SIZE]; + int mute; + int filterEnabled; + unsigned char filterReg; /* Used to restore value */ +/* Statistics stuff */ + int maxDelay; + int minDelay; + int writes; + int reads; + int seconds; + int longDelay; + int longDelays; + int shortDelay; + int shortDelays; + int noDelay; + int noDelays; + int jitter; + +/* ReSID style faked reads */ + unsigned char fakedRead; + int fakedDecay; + +/* read write callbacks */ + void (*poke) (struct sid_d *sid, unsigned char reg, + unsigned char value); + unsigned char (*peek) (struct sid_d *sid, unsigned char reg); +} sid_d; + + +/* Function prototypes */ +static loff_t hsid_llseek (struct file *file, loff_t offset, + int origin); +static ssize_t hsid_read (struct file *file, char *buf, + size_t count, loff_t *ppos); +static int hsid_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static void hsid_write_pr (sid_d *sid, __u32 cmd); +#ifdef KERNEL_2_2 +static int hsid_read_proc (char *page, char **start, off_t off, + int count, int unused); +#else +static int hsid_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data); +#endif + + +/* Global data */ +#ifdef KERNEL_2_2 +static struct proc_dir_entry hsid_proc_entry = +{ + 0, /* low_ino: inode is dynamic */ + 7, "hardsid", /* length of name and name */ + S_IFREG | S_IRUGO, /* mode */ + 1, 0, 0, /* nlinks, owner, group */ + 0, /* size -- not used */ + NULL, /* operations -- use default */ + &hsid_read_proc, /* function used to read data */ + /* nothing more */ +}; +#endif /* KERNEL_2_2 */ + +static DECLARE_WAIT_QUEUE_HEAD(hsid_wait); +static spinlock_t hsid_lock; +static spinlock_t hsid_reg_lock; +static struct fasync_struct *hsid_async_queue; +sid_d *sid_data[HSID_MAX_SIDS]; +static struct task_struct *thread; +static struct semaphore *notify; +static int active,rmmod; +static struct semaphore todoSem; +static int sid_numSIDs; +static int sid_open; +static sid_d sid_setup; +static int isa_allocated; +static int pci_allocated; +/* End of Globals */ + + +static void hsid_poke_isa_slowaccess (sid_d *sid, unsigned char reg, + unsigned char value) +{ + unsigned short port = sid->port; + outb(value, port); + outb(reg | (sid->chip << 6), port+1); +} + +static void hsid_poke_isa (sid_d *sid, unsigned char reg, + unsigned char value) +{ + outw( (sid->chip <<14) | (reg << 8) | value, sid->port); +} + +static void hsid_poke_cwmk3 (sid_d *sid, unsigned char reg, + unsigned char value) +{ + unsigned short port = sid->port; + outb(value, port + 0xd8); + outb(reg, port + 0xdc); +} + +static void hsid_poke_pci (sid_d *sid, unsigned char reg, + unsigned char value) +{ + outw( (sid->chip <<14) | (reg << 8) | value, sid->port+3); +} + +/* When reading a register OR 0x20 to the register value */ +static unsigned char hsid_peek_isa (sid_d *sid, unsigned char reg) +{ + unsigned short port = sid->port; + outb( reg | 0x20 | (sid->chip << 6), port+1); + udelay(2); + return inb(port); +} + +static unsigned char hsid_peek_cwmk3 (sid_d *sid, unsigned char reg) +{ + unsigned short port = sid->port; + outb( reg | 0x20, port + 0xdc); + udelay(2); + return inb(port + 0xd8); +} + +/* When reading a register OR 0x20 to the register value */ +static unsigned char hsid_peek_pci (sid_d *sid, unsigned char reg) +{ + unsigned short port = sid->port2 + 2; + unsigned char ret; + outb (reg | 0x20 | (sid->chip << 6), sid->port+4); + udelay (2); + outb (0x20, port); + ret = inb (sid->port); + outb (0x80, port); + return ret; +} + +static void hsid_delay (sid_d *sid, __u32 delay) +{ + hsid_write_pr (sid, (delay << 16) | 0x1f00); + + { /* Support ReSID style faked reads */ + __u32 decay = sid->fakedDecay; + sid->fakedDecay -= delay; + if (delay > decay) + { + sid->fakedDecay = 0; + sid->fakedRead = 0; + } + } +} + + +/* Reset the SID chip */ +static void hsid_reset(sid_d *sid, unsigned char vol) +{ + int i; + for (i = 0; i < 0x18; i++ ) + { + sid->poke(sid, i, 0); + udelay(2); + } + /* Set the volume */ + sid->poke(sid, i, vol); + udelay(2); +} + +/* The main worker thread, also known as ksidd */ +static int hsid_thread(void* data) +{ + sid_d** sids = (sid_d**)data; + sid_d* sid; + __u32 cmd, delay; + int clocks; + struct timeval tv; + int delayed; + int next, nextTime, wait, i; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) + daemonize(); +#ifdef KERNEL_2_4 + reparent_to_init(); +#endif + sigfillset(¤t->blocked); + strcpy(current->comm, "ksidd"); +#else + daemonize("ksidd"); +#endif + thread = current; + + /* We need high priority, so we go to real-time priority */ + thread->policy = SCHED_FIFO; + thread->rt_priority = 1; +#ifdef KERNEL_2_2 + thread->priority = renice; +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,19)) + set_user_nice(current, renice); +#else + thread->nice = renice; +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) + current->need_resched = 1; +#endif + + /* Notify the parent */ + if(notify != NULL) + up(notify); + + active = 1; + for(;;) + { + if (rmmod || signal_pending(current)) + break; + + /* We sit here waiting for something to do */ + down_interruptible(&todoSem); + + if (rmmod || signal_pending(current)) + break; + + /* Find the next write we should do */ + next = -1; + nextTime = 0; + + do_gettimeofday(&tv); + + for ( i = 0; i < sid_numSIDs; i++ ) + { + sid = sids[i]; + if ( sid->status & HSID_IS_OPEN ) + { + clocks = (tv.tv_sec - sid->tv.tv_sec) * 1000000 + + ( tv.tv_usec - sid->tv.tv_usec); + + cmd = sid->buffer[sid->curCommand]; + delay = cmd >> 16; + wait = (sid->cycles + delay) - clocks; + + if ( (atomic_read(&sid->todoSem.count) > 0) && + ( next == -1 || wait < nextTime ) ) + { + next = i; + nextTime = wait; + } + } + } + + if ( next == -1 ) + { + /* False alarm, possibly reset */ + continue; + } + + sid = sids[next]; + + down(&sid->todoSem); + cmd = sid->buffer[sid->curCommand]; + sid->curCommand++; + sid->curCommand &= HSID_BUFFER_SIZE - 1; + + delay = cmd >> 16; + sid->cycles += delay; + + if (((sid->minDelay == -1) || (delay < sid->minDelay)) && (delay != 0)) + sid->minDelay = delay; + if ( sid->maxDelay == -1 || delay > sid->maxDelay ) + sid->maxDelay = delay; + sid->writes++; + + /* We make a brute approximation of SID clock as + 1 MHz (which aligns nicely with 1 usec resolution + of gettimeofday */ + + delayed = 0; + + /* Check how much time has passed since previous write */ + clocks = (tv.tv_sec - sid->tv.tv_sec) * 1000000 + + ( tv.tv_usec - sid->tv.tv_usec); + + memcpy(&sid->tv, &tv, sizeof(tv)); + + sid->cycles -= clocks; + + while ( sid->cycles > 1000000 / HZ ) + { + /* Long wait, schedule */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(sid->cycles / 1000000); + /* Now we should only have to delay a short while if at all */ + do_gettimeofday(&tv); + + /* Update cycle status */ + clocks = (tv.tv_sec - sid->tv.tv_sec) * 1000000 + + ( tv.tv_usec - sid->tv.tv_usec); + + memcpy(&sid->tv, &tv, sizeof(tv)); + sid->longDelay++; + sid->longDelays += sid->cycles; + sid->cycles -= clocks; + delayed = 1; + } + + if ( sid->cycles > 4 ) + { + /* Short delay */ + udelay(sid->cycles); + sid->shortDelay++; + sid->shortDelays += sid->cycles; + } + else + { + if ( !delayed ) + { + /* Always at least a small delay (4 cycles here ) + so SID can manage it */ + udelay(4); + sid->noDelay++; + sid->noDelays += sid->cycles; + } + } + + switch( (cmd >> 8) & 0x1f ) + { + case 4: + cmd &= ~( sid->mute & 1); + break; + case 0xb: + cmd &= ~(( sid->mute >> 1) & 1); + break; + case 0x12: + cmd &= ~(( sid->mute >> 2) & 1); + break; + case 0x17: + sid->filterReg = cmd & 0xff; + if (!sid->filterEnabled) + cmd &= ~((__u32) 0x0f); + default: + break; + } + + /* Ignore registers greater than 0x18 as they are either read only + * or not used + */ + if ( (cmd & 0x1f00) <= 0x1800 ) + { + unsigned char reg = (unsigned char) (cmd >> 8), + data = (unsigned char) cmd & 0xff; + + spin_lock(&hsid_reg_lock); + switch (sid->card) + { + case SID_CARD_QUATTRO: + case SID_CARD_PCI_QUATTRO: + if (quattrohack) + { + int chip = sid->chip; + sid->chip = 0; + sid->poke (sid, reg, data); + sid->chip = 1; + sid->poke (sid, reg, data); + sid->chip = 2; + sid->poke (sid, reg, data); + sid->chip = 3; + sid->poke (sid, reg, data); + sid->chip = chip; + break; + } + default: + sid->poke (sid, reg, data); + } + spin_unlock(&hsid_reg_lock); + } + do_gettimeofday(&tv); + + /* Update cycle status */ + clocks = (tv.tv_sec - sid->tv.tv_sec) * 1000000 + + ( tv.tv_usec - sid->tv.tv_usec); + + memcpy(&sid->tv, &tv, sizeof(tv)); + sid->cycles -= clocks; + + sid->jitter += sid->cycles; + if ( sid->cycles < 0 ) + sid->cycles = 0; + + /* Free one buffer item */ + up(&sid->bufferSem); + } + + /* Off we go */ + active = 0; + thread = NULL; + + if(notify != NULL) + up(notify); + + return 0; +} + +/* + * Now all the various file operations that we export. + */ + +static loff_t hsid_llseek(struct file *file, loff_t offset, int origin) +{ + switch(origin) + { + case 0: + file->f_pos = offset; + return file->f_pos; + case 1: + file->f_pos += offset; + return file->f_pos; + default: + return -EINVAL; + } +} + + +static void hsid_write_pr(sid_d *sid, __u32 cmd) +{ + down(&sid->bufferSem); + sid->buffer[sid->lastCommand] = cmd; + sid->lastCommand++; + sid->lastCommand &= HSID_BUFFER_SIZE - 1; + up(&sid->todoSem); + up(&todoSem); +} + + +static ssize_t hsid_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + int ret = 0; + size_t bytes; + __u32 buf; + const char *p = buffer; + size_t c = count; + sid_d* sid; + + if ( GETMINOR(file) >= sid_numSIDs ) + return -EFAULT; + + sid = sid_data[GETMINOR(file)]; + + while (c > 0) + { + bytes = MIN(c, sizeof(buf)); + + bytes -= copy_from_user(&buf, p, bytes); + if (!bytes) + { + ret = -EFAULT; + break; + } + c -= bytes; + p += bytes; + + /* We want multiples of 4 bytes here */ + if ( bytes != 4 ) + break; + + /* Support ReSID based faked reads */ + sid->fakedRead = (unsigned char) (buf & 0xff); + sid->fakedDecay = 0x2000; /* Clock cycles */ + + /* Command structure: + bits 31-16: 16 bit delay timer value in C64 clock cycles + bits 15-13: reserved, keep zero + bits 12-8: SID register number + bits 7-0: Data + */ + hsid_write_pr(sid, buf); + } + + if (p == buffer) + { + return (ssize_t)ret; + } + else + { + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(file->f_dentry->d_inode); + return (ssize_t)(p - buffer); + } +} + + +static unsigned char hsid_read_pr(sid_d *sid, __u32 cmd) +{ + unsigned char t; + + sid->reads++; + + if (cmd & 0xFFFF0000) + { /* Perform delay */ + hsid_delay (sid, cmd >> 16); + } + + if ( (cmd & 0x1f00) < 0x1900 || + (cmd & 0x1f00) > 0x1c00 ) + { /* ReSID style faked read */ + return sid->fakedRead; + } + + /* Wait until all writes are done */ + while ( atomic_read(&sid->todoSem.count) > 0 ) + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + udelay(2); + + spin_lock(&hsid_reg_lock); + t = sid->peek(sid, (cmd >> 8) & 0x1f); + spin_unlock(&hsid_reg_lock); + return t; +} + + +static ssize_t hsid_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + unsigned long i = *ppos; + sid_d* sid; + + if ( GETMINOR(file) >= sid_numSIDs ) + return -EFAULT; + + sid = sid_data[GETMINOR(file)]; + + /* We want only 1 byte reads */ + if ( count != 1 ) + return -EFAULT; + + if ( i > 0x1f ) + return -EFAULT; + + /* + if (verify_area(VERIFY_WRITE,buf,count)) + return -EFAULT; + */ + + if (__put_user( hsid_read_pr(sid, i << 8), buf) < 0) + return -EFAULT; + + *ppos = i + 1; + return 1; +} + +static int hsid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + sid_d* sid; + int t; + + uint32_t _minor; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + _minor = iminor(inode); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) + _minor = minor(inode->i_rdev); +#else + _minor = MINOR(inode->i_rdev); +#endif +#endif + + if ( _minor >= sid_numSIDs ) + return 0; + + sid = sid_data[_minor]; + + switch(cmd) + { + case HSID_IOCTL_RESET: + sid->curCommand = 0; + sid->lastCommand = 0; + sid->cycles = 0; + sid->filterReg = 0; + + sid->reads = 0; + sid->writes = 0; + sid->minDelay = -1; + sid->maxDelay = -1; + sid->longDelay = 0; + sid->shortDelay = 0; + sid->noDelay = 0; + sid->longDelays = 0; + sid->shortDelays = 0; + sid->noDelays = 0; + sid->jitter = 0; + + sid->fakedRead = 0; + sid->fakedDecay = 0; + + do_gettimeofday(&sid->tv); + sid->seconds = sid->tv.tv_sec; + + t = atomic_read(&todoSem.count); + t -= atomic_read(&sid->todoSem.count); + atomic_set(&todoSem.count, t); +#ifdef KERNEL_2_2 + init_MUTEX(&sid->bufferSem); + init_MUTEX(&sid->todoSem); +#endif + sema_init(&sid->bufferSem, HSID_BUFFER_SIZE); + sema_init(&sid->todoSem, 0); + + spin_lock(&hsid_reg_lock); + hsid_reset(sid, arg & 0x0f); + spin_unlock(&hsid_reg_lock); + break; + + case HSID_IOCTL_FIFOSIZE: + return put_user(HSID_BUFFER_SIZE, (int*)arg); + + case HSID_IOCTL_FIFOFREE: + t = atomic_read(&sid->bufferSem.count); + return put_user(t, (int*)arg); + + case HSID_IOCTL_SIDTYPE: + return put_user(sid->type, (int*)arg); + + case HSID_IOCTL_CARDTYPE: + return put_user(sid->card, (int*)arg); + + case HSID_IOCTL_MUTE: + sid->mute = arg & 0x7; + break; + + case HSID_IOCTL_NOFILTER: + arg = (arg != 0); + if (arg == sid->filterEnabled) + break; + sid->filterEnabled = arg; + if (arg) /* Enabled, schedule restore of filters */ + hsid_write_pr(sid, sid->filterReg | 0x1700); + break; + + case HSID_IOCTL_FLUSH: + /* Wait until all writes are done */ + while ( atomic_read(&sid->todoSem.count) > 0 ) + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + break; + + case HSID_IOCTL_DELAY: + hsid_delay (sid, (__u32) arg); + break; + + case HSID_IOCTL_READ: + { + uint32_t parameter; + if ( get_user( parameter, (int*)arg) ) + return -EFAULT; + return put_user( hsid_read_pr(sid, parameter), (int*) arg ); + } + default: + printk(KERN_ERR "hardsid: unknown ioctl %x\n", cmd); + break; + } + return 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous data on an open, and clean up things on + * a close. + */ + +/* We use hsid_lock to protect against concurrent opens. So the BKL is not + * needed here. Or anywhere else in this driver. */ +static int hsid_open(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(sem); + sid_d* sid; + uint32_t _minor; + + spin_lock(&hsid_lock); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + _minor = iminor(inode); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) + _minor = minor(inode->i_rdev); +#else + _minor = MINOR(inode->i_rdev); +#endif +#endif + + if ( _minor >= sid_numSIDs ) + goto out_busy; + + sid = sid_data[_minor]; + if(sid->status & HSID_IS_OPEN) + goto out_busy; + + sid->status |= HSID_IS_OPEN; + sid->curCommand = 0; + sid->lastCommand = 0; + sid->cycles = 0; + sid->mute = 0; /* All 3 voices on */ + sid->filterEnabled = 1; + sid->filterReg = 0; + + sid->reads = 0; + sid->writes = 0; + sid->minDelay = -1; + sid->maxDelay = -1; + sid->longDelay = 0; + sid->shortDelay = 0; + sid->noDelay = 0; + sid->longDelays = 0; + sid->shortDelays = 0; + sid->noDelays = 0; + sid->jitter = 0; + do_gettimeofday(&sid->tv); + sid->seconds = sid->tv.tv_sec; + +#ifdef KERNEL_2_2 + init_MUTEX(&sid->bufferSem); + init_MUTEX(&sid->todoSem); +#endif + sema_init(&sid->bufferSem, HSID_BUFFER_SIZE); + sema_init(&sid->todoSem, 0); + + sid_open++; + + spin_unlock(&hsid_lock); + + if ( thread == NULL ) + { +#ifdef KERNEL_2_2 + init_MUTEX(&todoSem); +#endif + sema_init(&todoSem, 0); + + rmmod = 0; + notify = &sem; + kernel_thread(hsid_thread, (void *)sid_data, 0); + down(&sem); + notify = NULL; + } + + return 0; + + out_busy: + spin_unlock(&hsid_lock); + return -EBUSY; +} + +static int hsid_fasync (int fd, struct file *filp, int on) + +{ + return fasync_helper (fd, filp, on, &hsid_async_queue); +} + +static int hsid_release(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(sem); + sid_d* sid; + uint32_t _minor; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + _minor = iminor(inode); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) + _minor = minor(inode->i_rdev); +#else + _minor = MINOR(inode->i_rdev); +#endif +#endif + + if ( _minor >= sid_numSIDs ) + return 0; + + sid = sid_data[_minor]; + if( !(sid->status & HSID_IS_OPEN) ) + return 0; + + sid_open--; + + if (thread != 0 && sid_open == 0) + { + notify = &sem; + rmmod = 1; + up(&todoSem); + down(&sem); + notify = NULL; + rmmod = 0; + } + + /* Reset the chip for returning to the sid pool */ + switch (sid->card) + { + case SID_CARD_QUATTRO: + case SID_CARD_PCI_QUATTRO: + if (quattrohack) + { + int chip = sid->chip; + sid->chip = 0; + hsid_reset(sid, 0); + sid->chip = 1; + hsid_reset(sid, 0); + sid->chip = 2; + hsid_reset(sid, 0); + sid->chip = 3; + hsid_reset(sid, 0); + sid->chip = chip; + break; + } + default: + hsid_reset(sid, 0); + } + + /* No need for locking -- nobody else can do anything until this rmw is + * committed */ + sid->status &= ~HSID_IS_OPEN; + + return 0; +} + +static unsigned int hsid_poll(struct file *file, poll_table *wait) +{ + unsigned long l = 0; + + poll_wait(file, &hsid_wait, wait); + + spin_lock(&hsid_lock); + +/* TODO */ + + spin_unlock(&hsid_lock); + + if (l != 0) + return POLLIN | POLLRDNORM; + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations hsid_fops = +{ +#ifndef KERNEL_2_2 + owner: THIS_MODULE, +#endif + llseek: hsid_llseek, + read: hsid_read, + write: hsid_write, + poll: hsid_poll, + ioctl: hsid_ioctl, + open: hsid_open, + release: hsid_release, + fasync: hsid_fasync, +}; + + +static sid_type hsid_detect(sid_d *sid) +{ + int i, val; + + /* Reset the chip */ + hsid_reset(sid, 0); + + /* Set frequency */ + sid->poke(sid, 0xf, 0xff); + udelay(4); + + /* Set TEST bit to reset the noise generator */ + sid->poke(sid, 0x12, 0x88); + udelay(10); + + /* Clear TEST */ + sid->poke(sid, 0x12, 0x80); + udelay(50); + + /* Read the current oscillator 3 output */ + val = sid->peek(sid, 0x1b); + for ( i = 0; i < 0xffff; i++ ) + { + /* If the value changes, we might have a SID here */ + if ( sid->peek(sid, 0x1b) != val ) + break; + } + /* If we looped all the way, there is no SID */ + if ( i == 0xffff ) + return SID_NONE; + + /* Reset the chip */ + hsid_reset(sid, 0); + + sid->poke(sid, 0xf, 0xff); + udelay(4); + + /* Set combined waveform which doesn't work on 6581 */ + sid->poke(sid, 0x12, 0x30); + udelay(50); + + for ( i = 0; i < 0xffff; i++ ) + { + if ( ( sid->peek(sid, 0x1b) & 0x80) != 0 ) + break; + } + + hsid_reset(sid, 0); + + /* If the previous loop didn't finish, we have a 8580, otherwise 6581 */ + if ( i == 0xffff ) + return SID_6581; + else + return SID_8580; +} + +static int hsid_detect_chips (sid_d *sid, int chips) +{ + int i, detected = 0; + sid_d *newsid; + for ( i = 0; i < chips; i++ ) + { + sid->chip = (unsigned char) i; + sid->type = hsid_detect(sid); + if (sid->type == SID_NONE) + { + if ( detecthack ) + sid->type = SID_6581; + else + continue; + } + + newsid = sid_data[sid_numSIDs] = + kmalloc(sizeof(sid_d), GFP_KERNEL); + memcpy (newsid, sid, sizeof (sid_d)); + sid_numSIDs++; + printk(KERN_INFO "%s card with %s as chip %d " + "detected @ %#x\n", sid_card[sid->card], sid->type == SID_6581? "6581":"8580", + i, sid->port); + detected = 1; + } + return detected; +} + +static void hsid_register(void) +{ +#ifdef CONFIG_DEVFS_FS + char device_name[16]; + int i; + for (i = 0; i < sid_numSIDs; i++) + { + sid_d *sid = sid_data[i]; + if ( !(sid->status & HSID_IS_REGISTERED) ) + { + sprintf(device_name, "sid%d", i); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) + hsid_handles[i] = devfs_register(NULL, device_name, DEVFS_FL_DEFAULT, + major, i, + S_IFCHR | S_IRUGO | S_IWUGO, + &hsid_fops, NULL); +#else + devfs_mk_cdev(MKDEV(major, i), S_IFCHR | S_IRUGO | S_IWUGO, + device_name, i); +#endif + sid->status |= HSID_IS_REGISTERED; + } + } +#endif +} + +static int hsid_probe_isa (sid_d *sid) +{ + int i, val; + + /* Reset the first two chips (doesn't matter if there isn't any as + chip 1) */ + sid->chip = 0; + hsid_reset(sid, 0); + sid->chip = 1; + hsid_reset(sid, 0); + sid->chip = 0; + + /* Program chip 0 for noise */ + + /* Set frequency */ + sid->poke (sid, 0xf, 0xff); + udelay(4); + + /* Set TEST bit to reset the noise generator */ + sid->poke (sid, 0x12, 0x88); + udelay(10); + + /* Clear TEST */ + sid->poke (sid, 0x12, 0x80); + udelay(50); + + + /* Read the current oscillator 3 output from chip 0 */ + val = sid->peek (sid, 0x1b); + for ( i = 0; i < 0xffff; i++ ) + { + /* If the value changes, we have have a SID here */ + if ( sid->peek (sid, 0x1b) != val ) + break; + } + + /* If we looped all the way, there is no SID here */ + if ( i == 0xffff ) + return SID_CARD_NONE; + + /* Read the current oscillator 3 output from possible chip 1 */ + sid->chip = 1; + val = sid->peek (sid, 0x1b); + for ( i = 0; i < 0xffff; i++ ) + { + /* If the value changes, we have a regular HardSID */ + if ( sid->peek (sid, 0x1b) != val ) + break; + } + /* Reset the chip */ + sid->chip = 0; + hsid_reset(sid, 0); + + /* If we looped all the way, it is a Quattro */ + if ( i == 0xffff ) + return SID_CARD_QUATTRO; + return SID_CARD_HARDSID; +} + +static int hsid_probe_pci(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + int iobase1, iobase2; + sid_d *sid = &sid_setup; + int err; + u8 rev; + sid_card_type card; + + /* This need testing */ + pci_read_config_byte (pcidev, PCI_REVISION_ID, &rev); + switch (rev) + { + case 1: + card = SID_CARD_PCI_HARDSID; + break; + case 2: + card = SID_CARD_PCI_QUATTRO; + break; + default: + /* The card is not ours or is a new model ... */ + return 0; + } + + if(( err = pci_enable_device(pcidev) )) + { + printk(KERN_ERR "hardsid: could not enable device\n"); + return err; + } + + /* Allocate all card resources */ + if ( (err = pci_request_regions(pcidev, "hardsid")) ) + return err; + + /* get the io addresses */ + iobase1 = pci_resource_start(pcidev, 0); + if (pci_resource_len(pcidev, 1)) + iobase2 = pci_resource_start(pcidev, 1); + else + iobase2 = iobase1 + 0x0400; + + // Initialize PCI controller + outb(0xff, iobase1 + 0x0); + outb(0x80, iobase2 + 0x2); + outb(0x00, iobase1 + 0x2); + udelay (100); + outb(0x24, iobase1 + 0x2); + + memset (sid, 0, sizeof (sid_d)); + sid->card = card; + sid->peek = hsid_peek_pci; + sid->poke = hsid_poke_pci; + sid->port = (unsigned short) iobase1; + sid->port2 = (unsigned short) iobase2; + sid->pcidev = pcidev; + + if (sid->card == SID_CARD_PCI_QUATTRO) + err = hsid_detect_chips(sid, HSID_MAX_SIDS_PER_CARD); + else + err = hsid_detect_chips(sid, 1); + + if (err == 0) + { + printk(KERN_ERR "hardsid: no SID detected on card in 0x%04x\n", + sid->port); + pci_release_regions(pcidev); + sid->pcidev = 0; + return -EIO; /* Correct for multiple cards? */ + } + + hsid_register (); + return 0; +} + +static int hsid_probe_cmk3(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + int cw_iobase; + sid_d *sid = &sid_setup; + int err; + + if(( err = pci_enable_device(pcidev) )) + { + printk(KERN_ERR "hardsid: could not enable device\n"); + return err; + } + + cw_iobase = pci_resource_start(pcidev, 0); + + if(!request_region(cw_iobase, 256, "hardsid")) + { + printk(KERN_ERR "hardsid: IO-ports 0x%04x-0x%04x in use\n", cw_iobase, cw_iobase+255); + return -EBUSY; + } + + // Initialize PCI controller + outb(0xf1, cw_iobase + 0x0); + outb(0x00, cw_iobase + 0x1); + outb(0x00, cw_iobase + 0x2); + outb(0x00, cw_iobase + 0x4); + outb(0x00, cw_iobase + 0x5); + outb(0x00, cw_iobase + 0x29); + outb(0x00, cw_iobase + 0x2b); + + // Init the rest + memset (sid, 0, sizeof (sid_d)); + sid->peek = hsid_peek_cwmk3; + sid->poke = hsid_poke_cwmk3; + sid->port = (unsigned short) cw_iobase; + sid->card = SID_CARD_CWMK3; + sid->type = hsid_detect(sid); + sid->pcidev = pcidev; + + if ( !hsid_detect_chips (sid, 1) ) + { + printk(KERN_ERR "hardsid: no SID detected on card in 0x%04x\n", + cw_iobase); + release_region(cw_iobase, 256); + sid->port = 0; + sid->pcidev = 0; + return -EIO; /* Correct for multiple cards? */ + } + + hsid_register (); + return 0; +} + +static int __init hsid_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + switch (pcidev->vendor) + { + case PCI_VENDOR_ID_INDIVIDUAL: + return hsid_probe_cmk3 (pcidev, pciid); + case PCI_VENDOR_ID_HARDSOFTWARE: + return hsid_probe_pci (pcidev, pciid); + default: + break; + } + return 0; +} + + +MODULE_DEVICE_TABLE(pci, id_table); + +/* @FIXME@ Need more ?? PCI devices are hotswapable */ +static struct pci_driver hsid_driver = +{ + name: "hardsid", + id_table: id_table, + probe: hsid_probe, +}; + +static void __exit hsid_exit (void); + +static int __init hsid_init(void) +{ + int i, ret; + sid_d *sid = &sid_setup; + int gotmajor; + + printk(KERN_INFO "HardSID Driver v" HSID_VERSION "\n"); + sid_numSIDs = 0; + sid_open = 0; + isa_allocated = 0; + pci_allocated = 0; + spin_lock_init(&hsid_lock); + spin_lock_init(&hsid_reg_lock); + + /* Once registered always leave them that way till module is unloaded. + * This allows support for PCI hotswappable support */ + gotmajor = register_chrdev(major, "hardsid", &hsid_fops); + if ( gotmajor < 0 ) + { + printk(KERN_ERR "hardsid: could not register major number %d.\n", + major); + return -EIO; + } + /* If major was 0, we asked for a dynamic major number, use it */ + if ( major == 0 ) + { + major = gotmajor; + printk(KERN_INFO "Using major number %d.\n", major); + } + +#ifdef CONFIG_DEVFS_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) + if (devfs_register_chrdev(major, "hardsid", &hsid_fops)) + { + printk(KERN_ERR "hardsid: could not register major number %d.\n", + major); + return -EIO; + } +#endif +#endif + +#ifdef CONFIG_PROC_FS +#ifndef KERNEL_2_2 + create_proc_read_entry ("hardsid", 0, 0, hsid_read_proc, NULL); +#else + proc_register(&proc_root, &hsid_proc_entry); +#endif +#endif + + do + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,55) + if (check_region (io, ioextent)) + { + printk(KERN_ERR "hardsid: I/O port #%x is not free.\n", io); + break; + } +#endif + if(!request_region(io, ioextent, "hardsid")) + { + printk(KERN_ERR "hardsid: I/O port #%x is not free.\n", io); + break; + } + isa_allocated = 1; + + memset (sid, 0, sizeof (sid_d)); + sid->peek = hsid_peek_isa; + sid->poke = hsid_poke_isa; + if (slowaccess) + sid->poke = hsid_poke_isa_slowaccess; + + for ( i = io; i < io + ioextent; i += 2) + { + sid->port = (unsigned short) i; + sid->card = hsid_probe_isa(sid); + + if ( detecthack) + if ( i == io ) sid->card = SID_CARD_HARDSID; + + switch(sid->card) + { + case SID_CARD_QUATTRO: + (void) hsid_detect_chips (sid, HSID_MAX_SIDS_PER_CARD); + break; + + case SID_CARD_HARDSID: + (void) hsid_detect_chips (sid, 1); + break; + + default: + break; + } + } + + if ( sid_numSIDs ) + hsid_register (); + else + { + release_region(io, ioextent); + isa_allocated = 0; + } + } while (0); + + + ret = pci_module_init(&hsid_driver); + pci_allocated = (ret == 0); + if (sid_numSIDs || pci_allocated) + return 0; + + printk(KERN_ERR "hardsid: could not find any HardSID cards.\n"); + /* Clean up */ + hsid_exit (); + return ret; +} + +static void __exit hsid_exit (void) +{ + int i; + + if ( isa_allocated ) + release_region(io, ioextent); + + if ( pci_allocated ) + pci_unregister_driver(&hsid_driver); + + for ( i = 0; i < sid_numSIDs; i++ ) + { + switch( sid_data[i]->card ) + { + case SID_CARD_CWMK3: + if ( sid_data[i]->port ) + release_region(sid_data[i]->port, 256); + break; + case SID_CARD_PCI_HARDSID: + case SID_CARD_PCI_QUATTRO: + if ( sid_data[i]->pcidev ) + pci_release_regions(sid_data[i]->pcidev); + break; + default: + break; + } + kfree(sid_data[i]); + } +#ifdef CONFIG_PROC_FS +#ifndef KERNEL_2_2 + remove_proc_entry ("hardsid", NULL); +#else + proc_unregister(&proc_root, hsid_proc_entry.low_ino); +#endif +#endif + + unregister_chrdev(major, "hardsid"); +#ifdef CONFIG_DEVFS_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) + devfs_unregister_chrdev(major, "hardsid"); +#endif + for ( i = 0; i < sid_numSIDs; i++ ) + { + sid_d *sid = sid_data[i]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) + if ( hsid_handles[i] ) + devfs_unregister(hsid_handles[i]); +#else + char device_name[16]; + sprintf(device_name, "sid%d", i); + devfs_remove(device_name, i); +#endif + sid->status &= ~HSID_IS_REGISTERED; + } +#endif +} + + +/* + * Info exported via "/proc/driver/hardsid". + */ + +static int hsid_proc_output (char *buf) +{ + char *p; + struct timeval tv; + int rps, wps, lps, sps, nps; + int ald, asd, and, jpa; + sid_d* sid; + int i; + + do_gettimeofday(&tv); + + p = buf; + + for ( i = 0; i < sid_numSIDs; i++ ) + { + sid = sid_data[i]; + + rps = 0; wps = 0; lps = 0; sps = 0; nps = 0; + ald = 0; asd = 0; and = 0; jpa = 0; + + if ( tv.tv_sec > sid->seconds ) + { + rps = sid->reads / (tv.tv_sec - sid->seconds); + wps = sid->writes / (tv.tv_sec - sid->seconds); + lps = sid->longDelay / (tv.tv_sec - sid->seconds); + sps = sid->shortDelay / (tv.tv_sec - sid->seconds); + nps = sid->noDelay / (tv.tv_sec - sid->seconds); + } + if ( sid->longDelay ) + ald = sid->longDelays / sid->longDelay; + if ( sid->shortDelay ) + asd = sid->shortDelays / sid->shortDelay; + if ( sid->noDelay ) + and = sid->noDelays / sid->noDelay; + + if ( sid->writes + sid->reads != 0 ) + jpa = sid->jitter / ( sid->writes + sid->reads); + + p += sprintf(p, "%s configured for port %#x\n", + sid_card[sid->card], sid->port); + p += sprintf(p, "SID type %s\n", + sid->type == SID_6581? "6581": "8580"); + p += sprintf(p, "Muted: "); + + if (sid->mute) + { + int j; + for (j = 0; j < 3; j++) + { + if ((sid->mute >> j) & 1) + p += sprintf(p, "%d ", j + 1); + } + } + else + p += sprintf(p, "None"); + + p += sprintf(p, "\nFilter: %s\n", sid->filterEnabled ? "Enabled" : "Disabled"); + p += sprintf(p, "Writes: %8d Reads: %8d\n", sid->writes, sid->reads); + p += sprintf(p, "Per/s: %8d %8d\n", wps, rps); + p += sprintf(p, "Delays: %8d - %8d\n", sid->minDelay, + sid->maxDelay); + p += sprintf(p, "Long: %8d avg. %8d %8d/s\n", sid->longDelay, ald, + lps); + p += sprintf(p, "Short: %8d avg. %8d %8d/s\n", sid->shortDelay, asd, + sps); + p += sprintf(p, "None: %8d avg. %8d %8d/s\n", sid->noDelay, and, + nps); + p += sprintf(p, "Jitter: %8d avg. %8d\n", sid->jitter, jpa); + } + return p - buf; +} + + +#ifndef KERNEL_2_2 +static int hsid_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +#else +static int hsid_read_proc(char *page, char **start, off_t off, + int count, int unused) +#endif +{ + int len = hsid_proc_output (page); +#ifndef KERNEL_2_2 + if (len <= off+count) *eof = 1; +#endif + *start = page + off; + len -= off; + if (len > count) len = count; + if (len < 0) len = 0; + return len; +} + + +module_init(hsid_init); +module_exit(hsid_exit); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50) +EXPORT_NO_SYMBOLS; +#endif +MODULE_AUTHOR("Jarno Paananen"); +MODULE_DESCRIPTION("Driver for HardSID card"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#define MODULE_PARM(x,y) module_param(x,uint,0) + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "Base I/O-port (default=0x300), use only if" + "autodetection fails"); +MODULE_PARM(ioextent, "i"); +MODULE_PARM_DESC(ioextent, "Number of addresses to reserve (default=8), use" + "only if autodetection fails"); +MODULE_PARM(major, "i"); +MODULE_PARM_DESC(major, "Device major number to use (default=60)"); +MODULE_PARM(slowaccess, "i"); +MODULE_PARM_DESC(slowaccess, "Force use 8-bit I/O if the default 16-bit I/O" + "doesn't work"); +MODULE_PARM(detecthack, "i"); +MODULE_PARM_DESC(detecthack, "Force loading of the driver without a card," + "fakes a regular HardSID with 6581 at 0x300"); +MODULE_PARM(quattrohack, "i"); +MODULE_PARM_DESC(quattrohack, "Play the first SID data on all chips in a" + "Quattro"); +MODULE_PARM(renice, "i"); +MODULE_PARM_DESC(renice, "Set the kernel thread renice value"); + diff --git a/hardsid.h b/hardsid.h new file mode 100644 index 0000000..9516f81 --- /dev/null +++ b/hardsid.h @@ -0,0 +1,36 @@ +/* + * + * Linux HardSID Ioctl calls + * + * Copyright (C) 2001-2002 Jarno Paananen <jpaana@s2.org> + * Copyright (C) 2001 Simon White <s_a_white@email.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#ifndef _hardsid_h_ +#define _hardsid_h_ + +#ifdef __KERNEL__ +# include <linux/ioctl.h> +#else +# include <sys/ioctl.h> +#endif + +/* The ioctls we use */ +#define HSID_IOCTL_RESET _IOW('S', 0, int) +#define HSID_IOCTL_FIFOSIZE _IOR('S', 1, int) +#define HSID_IOCTL_FIFOFREE _IOR('S', 2, int) +#define HSID_IOCTL_SIDTYPE _IOR('S', 3, int) +#define HSID_IOCTL_CARDTYPE _IOR('S', 4, int) +#define HSID_IOCTL_MUTE _IOW('S', 5, int) +#define HSID_IOCTL_NOFILTER _IOW('S', 6, int) +#define HSID_IOCTL_FLUSH _IO ('S', 7) +#define HSID_IOCTL_DELAY _IOW('S', 8, int) +#define HSID_IOCTL_READ _IOWR('S', 9, int*) + +#endif /* _hardsid_h_ */ |