summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING340
-rw-r--r--Changelog111
-rw-r--r--Makefile17
-rw-r--r--README149
-rw-r--r--hardsid.c1840
-rw-r--r--hardsid.h36
6 files changed, 2493 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -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
+
diff --git a/README b/README
new file mode 100644
index 0000000..46c77c0
--- /dev/null
+++ b/README
@@ -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(&current->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_ */