summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjjoganic <jjoganic>2002-12-23 02:44:28 +0000
committerjjoganic <jjoganic>2002-12-23 02:44:28 +0000
commitba23e0362c2fd1aa72bea910812382d2bb9ed229 (patch)
treea6ce0383df6b9a1b459af8a6cbc3a70ad30f682b
downloadxf86-input-wacom-ba23e0362c2fd1aa72bea910812382d2bb9ed229.tar.gz
Initial revision
-rw-r--r--AUTHORS4
-rw-r--r--ChangeLog43
-rw-r--r--GPL340
-rw-r--r--LGPL504
-rw-r--r--Makefile.am2
-rw-r--r--NEWS10
-rw-r--r--README96
-rwxr-xr-xbootstrap4
-rw-r--r--configure.in262
-rw-r--r--docs/docs.html2217
-rw-r--r--docs/docs.txt2221
-rw-r--r--src/Makefile.am105
-rw-r--r--src/hid-core.c1386
-rw-r--r--src/hid-input.c434
-rw-r--r--src/hiddev.c686
-rw-r--r--src/usbmouse.c230
-rw-r--r--src/wacdump.c716
-rw-r--r--src/wacom.c690
-rw-r--r--src/wacscrn.c39
-rw-r--r--src/wacscrn.h32
-rw-r--r--src/wacserial.c966
-rw-r--r--src/wacserial.h115
-rw-r--r--src/wcm-beta.c618
-rw-r--r--src/wcm-beta.h67
-rw-r--r--src/xf86Wacom.c4070
25 files changed, 15857 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..aebcb81
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+There are a number of authors- just check the source files. Currently,
+this package is being maintained by:
+
+John E. Joganic <jej@j-arkadia.com>
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..f8d17ff
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,43 @@
+2002-12-21 John E. Joganic <john@joganic.com>
+
+ * Moved project to SourceForge as linuxwacom.
+
+2002-12-21 John E. Joganic <john@joganic.com>
+
+ * Added serial code to wacdump
+ * Added serial protocols IV 1.0 - 1.4.
+ * Added serial protocol V 1.0 - 1.1.
+ * Intuos mouse wheels are now positive when rolled backwards per PS/2
+ Intellimouse convention
+ * Added Intuos2 to xf86Wacom.c
+
+2002-12-17 John E. Joganic <john@joganic.com>
+
+ * Merged code from 2.4.20 hid-core.c
+ * Added configuration code to handle kernels still at 2.4.19
+ * Split wacdump curses code into wacscrn.c to solve namespace collision
+ * Added volito to wacom.c
+
+2002-12-15 John E. Joganic <john@joganic.com>
+
+ * Last minute fix to debugging level on 4D mouse wheel
+ * Added framework for new driver
+ * Added code for 4D mouse wheel in xf86Wacom.c
+
+2002-12-13 John E. Joganic <john@joganic.com>
+
+ * Added pen model numbers for Intuos2 grip pen and Intuos pen
+ * Added wcm-beta.c and wcm-beta.h shell
+ * Added relative events to wacdump
+ * Added relative bit to Intuos and Intuos2 reporting in wacom.c
+ * Fixed relative 2D mouse wheel reporting in wacom.c
+
+2002-11-27 John E. Joganic <john@joganic.com>
+
+ * Added exp subdirectory for experimental work
+ * Isolated xf86Wacom.c declarations to header file
+
+2002-11-24 John E. Joganic <john@joganic.com>
+
+ * Branched at 0.2.0
+ * Created configure-based build environment
diff --git a/GPL b/GPL
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/GPL
@@ -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/LGPL b/LGPL
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/LGPL
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; 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.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..148f1b7
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = src
+EXTRA_DIST = docs/docs.txt docs/docs.html GPL prebuilt/wacom_drv.o
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e221ed2
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,10 @@
+December 22, 2002 - John E. Joganic
+
+The wacpack project is being hosted on SourceForge as the Linux Wacom Project.
+This build is now distributed under linuxwacom.
+
+November 24, 2002 - John E. Joganic
+
+This marks the first release of wacpack with a configure-based build
+environment. Though it will not install anything, it is capable of
+building all the major drivers including wacom.o and wacom_drv.o.
diff --git a/README b/README
new file mode 100644
index 0000000..3f178ab
--- /dev/null
+++ b/README
@@ -0,0 +1,96 @@
+README
+November 24, 2002
+
+This package is not currently sanctioned by the original driver developers
+although several attempts to contact them have been made. Hopefully, this
+work will be rolled back into the main development trees. Until that time,
+I will be taking responsibility for this code. If you have any questions,
+comments, problems, patches, or complaints, send them to jej@j-arkadia.com.
+Please include the word WACOM in the subject to help me differentiate your
+email from the spam I usually receive.
+
+John E.Joganic
+jej@j-arkadia.com
+
+==============================================================================
+
+BUILDING FROM CVS:
+
+You should run bootstrap from the main directory to correctly initialize
+the project. From there, run configure as you would normally. Installation
+is not automatic so there is no make install target. Configure options are
+explained below.
+
+ $ ./bootstrap && ./configure && make
+
+==============================================================================
+
+BUILDING FROM TARBALL:
+
+As you would expect, run configure and make. Installation is not automatic
+so there is no make install target. Configure options are explained below.
+
+ $ ./configure && make
+
+==============================================================================
+
+CONFIGURE OPTIONS:
+
+This file contains hints for building the sources. There are a number of
+build targets, some of which are not built automatically. Here is the
+breakdown.
+
+ wacom.o: built by default (--enable-wacom, --disable-wacom)
+ wacdump: built by default (--enable-wacdump, --disable-wacdump)
+ hid.o: not built by default (--enable-hid, --disable-hid)
+ usbmouse.o: not built by default (--enable-usbmouse, --disable-usbmouse)
+ wacom_drv.o: not built by default (--enable-wacomdrv, --disable-wacomdrv)
+
+BUILDING:
+
+Generally, you run the configure script followed by make. This package
+will not attempt to install the drivers. Please see the documentation on
+the website for more information on where specific drivers belong.
+
+ [root@haku linuxwacom-dev] # ./configure
+ [root@haku linuxwacom-dev] # make
+
+There are many options, and configure will attempt to guess the correct
+values for most of them. By default, only the kernel driver and wacdump
+program are built.
+
+REQUIREMENTS:
+
+wacom.o, hid.o, and usbmouse are kernel modules and require the kernel
+sources to be present. On a Redhat 8.0 system with the kernel-sources
+RPM installed, the source directory is located at /usr/src/linux-2.4.
+The wacdump configuration script will search for this directory
+automatically as well as /usr/src/linux. You can override the directory
+using the --with-kernel=<directory> option.
+
+wacom_drv.o requires the XFree86 sources as well as a functional XF86
+build environment. Since this is a more complicated setup and few people
+are going to want to rebuild this driver from source, the wacom_drv.o file
+is supplied with the wacdump package in the prebuilt directory. It is
+compiled with gcc 3.2 under Redhat 8.0. If the prebuilt wacom_drv.o module
+will not work for you, see the section "Building wacom_drv.o From Scratch."
+
+BUILDING wacom_drv.o FROM SCRATCH
+
+You will need the XFree86 source code, and you will need to actually build
+it. The file xf86Version.h is generated during the build process and is a
+required dependency for building wacom_drv.o. The configure script will
+*not* attempt to build wacom-drv.o by default, but if you provide the
+--enable-wacomdrv and --with-xf86=<xf86_directory> options, configure will
+build wacom_drv.o during the normal build process.
+
+If the xf86Version.h file is located at:
+ /usr/local/src/XFree86-4.2.0/xc/programs/Xserver/include/xf86Version.h
+
+Then the correct configure options would be:
+ --enable-wacomdrv --with-xf86=/usr/local/src/XFree86-4.2.0
+
+==============================================================================
+
+WEBSITE: http://linux.joganic.com/wacom
+ HELP: jej@j-arkadia.com
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..6c5591c
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,4 @@
+aclocal
+autoheader
+autoconf
+automake --foreign --add-missing
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..333adfb
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,262 @@
+AC_INIT(src/wacom.c)
+AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE(linuxwacom, 0.3.4-beta)
+AM_MAINTAINER_MODE
+AC_PROG_CC
+AC_PROG_AWK
+
+XF86SUBDIR=xc/programs/Xserver
+
+dnl Targets
+WAC_PROGS=""
+WAC_MODULES=""
+WAC_XF86PROGS=""
+WAC_XF86MODULES=""
+WAC_EXPPROGS=""
+WAC_EXPMODULES=""
+WAC_PATCH_HID=""
+
+dnl Check architecture
+AC_MSG_CHECKING(for processor type)
+WAC_ARCH=`uname -m`
+AC_MSG_RESULT($WAC_ARCH)
+
+dnl Guess modversioning
+WAC_OPTION_MODVER=no
+AC_MSG_CHECKING(for kernel module versioning)
+kernelrel=`uname -r`
+moddir="/lib/modules/$kernelrel/kernel/drivers/usb"
+if test -f "$moddir/hid.o"; then
+ printk=`nm $moddir/hid.o | grep printk_R`
+ if test -n "$printk"; then
+ WAC_OPTION_MODVER=yes
+ AC_MSG_RESULT(yes)
+ else
+ printk=`nm $moddir/hid.o | grep printk`
+ if test -n "$printk"; then
+ AC_MSG_RESULT(no)
+ else
+ AC_MSG_RESULT(confused; assuming no)
+ WAC_OPTION_MODVER="unknown"
+ echo "***"; echo "*** WARNING:"
+ echo "*** printk symbol in hid.o not present"
+ echo "***"
+ fi
+ fi
+else
+ AC_MSG_RESULT(unknown; assuming no)
+ WAC_OPTION_MODVER="unknown"
+fi
+
+dnl Check for kernel build environment
+WAC_ENV_KERNEL=no
+AC_ARG_WITH(kernel,
+[ --with-kernel=dir Specify kernel source directory],
+[
+ WAC_KERNELDIR="$withval"
+ AC_MSG_CHECKING(for valid kernel source tree)
+ if test -f "$WAC_KERNELDIR/include/linux/input.h"; then
+ AC_MSG_RESULT(ok)
+ WAC_ENV_KERNEL=yes
+ else
+ AC_MSG_RESULT(missing input.h)
+ AC_MSG_ERROR("Unable to find $WAC_KERNELDIR/include/linux/input.h")
+ fi
+],
+[
+ dnl guess directory
+ AC_MSG_CHECKING(for kernel sources)
+ WAC_KERNELDIR="/usr/src/linux-2.4"
+ if test -f "$WAC_KERNELDIR/include/linux/input.h"; then
+ WAC_ENV_KERNEL=yes
+ AC_MSG_RESULT($WAC_KERNELDIR)
+ else
+ WAC_KERNELDIR="/usr/src/linux"
+ if test -f "$WAC_KERNELDIR/include/linux/input.h"; then
+ WAC_ENV_KERNEL=yes
+ AC_MSG_RESULT($WAC_KERNELDIR)
+ else
+ AC_MSG_RESULT(not found)
+ echo "***"
+ echo "*** WARNING:"
+ echo "*** Unable to guess kernel source directory"
+ echo "*** Looked at /usr/src/linux-2.4"
+ echo "*** Looked at /usr/src/linux"
+ echo "*** Kernel modules will not be built"
+ echo "***"
+ fi
+ fi
+])
+
+dnl Check for XFree86 build environment
+WAC_ENV_XF86=no
+AC_ARG_WITH(xf86,
+[ --with-xf86=dir Specify XF86 build directory],
+[
+ WAC_XF86DIR="$withval";
+ AC_MSG_CHECKING(for valid XFree86 build environment)
+ if test -f $WAC_XF86DIR/$XF86SUBDIR/include/xf86Version.h; then
+ WAC_ENV_XF86=yes
+ AC_MSG_RESULT(ok)
+ else
+ AC_MSG_RESULT("xf86Version.h missing")
+ AC_MSG_ERROR("Unable to find $WAC_XF86DIR/$XF86SUBDIR/include/xf86Version.h")
+ fi
+ WAC_XF86DIR=`(cd $WAC_XF86DIR; pwd)`
+])
+
+dnl Check for wacom build
+WAC_OPTION_WACOM=no
+AC_ARG_ENABLE(wacom,
+[ --enable-wacom Enable building wacom.o default=yes],
+ , enable_wacom=yes)
+if test "$enable_wacom" != "no"; then
+ if test "$WAC_ENV_KERNEL" != "yes"; then
+ echo "***"; echo "*** WARNING:";
+ echo "*** Unable to compile wacom.o without kernel build environment"
+ echo "*** wacom.o will not be built"
+ echo "***"
+ else
+ WAC_OPTION_WACOM=yes
+ WAC_MODULES="$WAC_MODULES wacom.o"
+ fi
+fi
+
+dnl Check for wacdump build
+WAC_OPTION_WACDUMP=no
+AC_ARG_ENABLE(wacdump,
+[ --enable-wacdump Enable building wacdump default=yes],
+ , enable_wacdump=yes)
+if test "$enable_wacdump" != "no"; then
+ WAC_OPTION_WACDUMP=yes
+ WAC_PROGS="$WAC_PROGS wacdump"
+fi
+
+dnl Check for hid build
+WAC_OPTION_HID=no
+AC_ARG_ENABLE(hid,
+[ --enable-hid Enable building hid.o default=no],
+ , enable_hid=no)
+if test "$enable_hid" != "no"; then
+ if test "$WAC_ENV_KERNEL" != "yes"; then
+ echo "***"; echo "*** WARNING:";
+ echo "*** Unable to compile hid.o without kernel build environment"
+ echo "*** hid.o will not be built"
+ echo "***"
+ else
+ WAC_OPTION_HID=yes
+ WAC_MODULES="$WAC_MODULES hid.o"
+ fi
+fi
+
+dnl Check for usbmouse build
+WAC_OPTION_USBMOUSE=no
+AC_ARG_ENABLE(usbmouse,
+[ --enable-usbmouse Enable building usbmouse.o default=no],
+ , enable_usbmouse=no)
+if test "$enable_usbmouse" != "no"; then
+ if test "$WAC_ENV_KERNEL" != "yes"; then
+ echo "***"; echo "*** WARNING:";
+ echo "*** Unable to compile usbmouse.o without kernel build environment"
+ echo "*** usbmouse.o will not be built"
+ echo "***"
+ else
+ WAC_OPTION_USBMOUSE=yes
+ WAC_MODULES="$WAC_MODULES usbmouse.o"
+ fi
+fi
+
+dnl Check for wacomdrv build
+WAC_OPTION_WACOMDRV=no
+AC_ARG_ENABLE(wacomdrv,
+[ --enable-wacomdrv Enable building wacom_drv.o default=no],
+ , enable_wacomdrv=no)
+if test "$enable_wacomdrv" != "no"; then
+ if test "$WAC_ENV_XF86" != "yes"; then
+ echo "***"; echo "*** WARNING:";
+ echo "*** Unable to compile wacom_drv.o without XF86 build environment"
+ echo "*** wacom_drv.o will not be built"
+ echo "***"
+ else
+ WAC_OPTION_WACOMDRV=yes
+ WAC_XF86MODULES="$WAC_XF86MODULES wacom_drv.o"
+ WAC_EXPMODULES="$WAC_EXPMODULES wacom_drv.o"
+ fi
+fi
+
+dnl Check for arch
+AC_ARG_WITH(arch,
+[ --with-arch Use specified architecture],
+[
+ WAC_ARCH=$withval
+])
+
+dnl Check for modver
+AC_ARG_ENABLE(modver,
+[ --enable-modver Enable kernel module versioning default=usually],
+ , enable_modver=$WAC_MODVER)
+if test "$enable_modver" = "no"; then
+ WAC_OPTION_MODVER=no
+ WAC_MODVER=""
+elif test "$enable-modver" = "yes"; then
+ WAC_OPTION_MODVER=yes
+ WAC_MODVER="-DCONFIG_MODVERSIONS"
+else
+ dnl not explicitly set, go with default
+ WAC_MODVER=""
+fi
+
+dnl Check for changed field names in hid-core.c
+if test "$WAC_OPTION_HID" = "yes"; then
+ AC_MSG_CHECKING(for updated field names in hid-core.c)
+ HIDCORE="$WAC_KERNELDIR/drivers/usb/hid-core.c"
+ if test -f "$HIDCORE"; then
+ PATCH_DRVALUE=`grep 'dr\\.wValue' "$HIDCORE" | wc -l`
+ if test "$PATCH_DRVALUE" -gt 0; then
+ AC_MSG_RESULT([yes, using 2.4.20])
+ AC_DEFINE(WAC_PATCH_DRVALUE,1,[Need the dr.wValue patch])
+ WAC_PATCH_HID="(value_patch) $WAC_PATCH_HID"
+ else
+ AC_MSG_RESULT([no, using 2.4.19])
+ AC_DEFINE(WAC_PATCH_DRVALUE,0,[Do not need the dr.wValue patch])
+ fi
+ else
+ AC_MSG_RESULT([missing, using 2.4.19])
+ AC_DEFINE(WAC_PATCH_DRVALUE,0,[Do not need the dr.wValue patch])
+ echo "***"
+ echo "*** Warning: unable to find hid-core.c in kernel tree"
+ echo "*** It was not possible to detect whether patches needed to be"
+ echo "*** applied. If the hid-core.c in this project does not build"
+ echo "*** correctly, you should resolve this problem first."
+ echo "***"
+ fi
+fi
+
+AC_SUBST(WAC_PROGS)
+AC_SUBST(WAC_MODULES)
+AC_SUBST(WAC_XF86PROGS)
+AC_SUBST(WAC_XF86MODULES)
+AC_SUBST(WAC_EXPPROGS)
+AC_SUBST(WAC_EXPMODULES)
+AC_SUBST(WAC_ARCH)
+AC_SUBST(WAC_MODVER)
+AC_SUBST(WAC_KERNELDIR)
+AC_SUBST(WAC_XF86DIR)
+
+AC_OUTPUT(Makefile src/Makefile)
+
+echo ""
+echo "----------------------------------------"
+echo " BUILD ENVIRONMENT:"
+echo " architecture - $WAC_ARCH"
+echo " module versioning - $WAC_OPTION_MODVER"
+echo " kernel - $WAC_ENV_KERNEL"
+echo " XFree86 - $WAC_ENV_XF86"
+echo ""
+echo " BUILD OPTIONS:"
+echo " wacom.o - $WAC_OPTION_WACOM"
+echo " wacdump - $WAC_OPTION_WACDUMP"
+echo " hid.o - $WAC_OPTION_HID $WAC_PATCH_HID"
+echo " usbmouse.o - $WAC_OPTION_USBMOUSE"
+echo " wacom_drv.o - $WAC_OPTION_WACOMDRV"
+echo "----------------------------------------"
diff --git a/docs/docs.html b/docs/docs.html
new file mode 100644
index 0000000..adbd29c
--- /dev/null
+++ b/docs/docs.html
@@ -0,0 +1,2217 @@
+<HTML>
+<HEAD>
+<TITLE>John's Linux USB Wacom Page</TITLE>
+</HEAD>
+<STYLE>
+ .diff { background: #EEEEFF }
+ .nav { background: #EEEEEE }
+</STYLE>
+<BODY>
+<CENTER><H1>John's Linux USB Wacom Page</H1>Last Modified: December 22, 2002 18:12<BR><A HREF="#changelog">Changelog</A></CENTER><BR><CENTER CLASS=nav>Navigation:
+<A HREF="/wacom/index.php/id/book">MAIN</A>&nbsp;&nbsp;&nbsp;<A HREF="/wacom/index.php/id/intro">NEXT</A>&nbsp;&nbsp;&nbsp;<A HREF="/wacom/index.php/toc">INDEX</A>&nbsp;&nbsp;&nbsp;<A HREF="/wacom/index.php/all"><B>ALL</B></A>&nbsp;&nbsp;&nbsp;</CENTER><BR><BR>
+<A NAME="intro">
+<H1>1.0 - Introduction</H1>
+<P>This document was written for three reasons: 1) to remind myself
+how I got my brand-new Wacom Intuos2 running on my Redhat 8.0 Linux box in
+case I have to do it again from scratch, 2) to help other people do the same,
+and 3) to help de-mystify the process by explaining why things work as they
+do, and where to look when things seem to be going wrong.
+
+<P>If you find an error, have a question, or have something to add, please
+contact me directly at
+<A HREF="mailto:jej@j-arkadia.com?subject=WACOM">jej@j-arkadia.com</A>.
+Furthermore, please add the word "WACOM" to the subject line. I get hundreds
+of messages a day, so that'll help me see your message amidst the spam.
+<A NAME="expect">
+<H2>1.1 - Expectations</H2>
+<H2>What You Can Expect Today</H2>
+
+<P>Depending on whether you are using serial or USB tablets, your experience
+will differ, but for the moment at least, the serial tablets have a generally
+simpler configuration. This document currently addresses only the USB
+tablets, but that will change in the future. Mr. Lepied's
+<A HREF="http://people.mandrakesoft.com/~flepied/projects/wacom/">XInput driver for XFree86</A> web page
+has a good description of the serial configuration process. The goal of
+this website is to make configuration and use of both tablet types as
+simple as possible. Here is what you can expect now:
+
+<H3>You Should Be Able to Get Your Tablet Working</H3>
+
+<P>Most all serial and USB tablets are working to a reasonably functional
+degree. I have an old ArtPad (1997) that does not initialize with the
+XFree86 driver, and USB Cintiq users are having a little trouble with the
+kernel drivers. Everyone else should be up and running, namely:
+
+<UL>
+<LI>PenPartner,
+<LI>Graphire,
+<LI>Graphire2,
+<LI>Intuos, and
+<LI>Intuos2
+</UL>
+
+<H3>All Your Fancy Styluses and Pointers Should Work</H3>
+
+<P>I have a collection of pointers, pens, and brushes to test; all of them
+work completely as one would expect. Users have reported success with
+their tools as well. I have not tested the airbrush yet, but presumably
+it works just fine. Tools that are known to work include:
+
+<UL>
+<LI>Graphire2 2D mouse (EC-120-0K),
+<LI>Intuos Pen (GP-300E-01H),
+<LI>Intuos2 2D mouse,
+<LI>Intuos2 4D mouse,
+<LI>Intuos2 Lens cursor,
+<LI>Intuos2 grip pen (XP-501E-00A),
+<LI>Intuos2 stroke pen (XP-120-00A),
+<LI>Intuos2 ink pen (XP-110-00A),
+</UL>
+
+<H3>You Will Need to Modify Your XF86Config File</H3>
+
+<P>This project is actively working to become a turn-key system. It will be
+awhile before major distributions like Redhat include these settings
+automatically for you. Until that time, you will need to make some changes
+to your XFree86 configuration files by hand.
+
+<H3>You May Need to Update Your Kernel</H3>
+
+<P>This project assumes that you have a kernel version of at least 2.4.18.
+While it may be possible to use this document with older kernels, the
+likelihood of success diminishes the farther back you go. Redhat 8.0
+ships with this kernel, and most of the major distributions are already
+past this point.
+
+<H3>You Will Need to Work With the Command Line</H3>
+
+<P>This document details a step-by-step process for setting up and diagnosing
+the tablet. There are no GUI tools to help you through this process- at
+least not today. Some familiarity with the command line is recommended
+since I provide enough information to get the task accomplished, but I do
+not explain how the command line itself operates. Any good book on UNIX
+will do, and I use the bash shell for all my work. If you are not using
+bash, you presumably know what you are doing already.
+
+<H2>What You Can Expect Soon</H2>
+
+<P>I have a near-term design goal to get all the tablets and their respective
+tools functioning using the original driver design. This currently includes
+the airbrush (which may work already), the Cintiq, and if possible and time
+permits, the lowly ArtPad. If there are any outstanding problems with the
+original driver, I will take care of them as they come up.
+
+<P>While the current way of configuring the tablet is sub-par in my opinion,
+it is also the established way of doing things. Merging large-scale changes
+back into either the Linux kernel or the XFree86 code tree will be difficult,
+if for no other reason than insufficient multi-platform testing. To solve
+this problem, the new functionality described below will be implemented in
+a separate project and accessed through the original driver as an advanced
+option. This will allow all the various platforms to use the new code or not
+depending on their own constraints and circumstances. It also allows for
+people to write their own tablet libraries without mucking around in the
+XFree86 code.
+
+<H2>The Next Steps</H2>
+
+<P>First and foremost, the tablet needs to be configurable without restarting
+X. By separating the tablet code from the low-level X code, it will be
+possible to talk directly to the tablet engine without interfering
+with the X server's operation. Additionally, it will be possible to use
+either the XInput configuration API or the tablet engine's API to access
+that functionality. The preferred way would probably be an X server
+extension, but today at least, that is outside of the scope of my interest.
+Ask me again in another month.
+
+<P>The next issue is plug and play. The USB tablets have the
+unfriendly habit of being unaccessible when they are unplugged and replugged.
+Since the X server holds the device open, the tablets cannot reconnect to
+their original position and get bumped to another event slot. The new
+tablet engine will address the problem, and can probably even auto-probe
+for the USB tablets.
+
+<P>Third, a GUI-based configuration tool should cap things off nicely. I
+intend to replicate the functionality of the Windows configuration tool.
+This includes diagnostics, pressure mappings, button settings, keyboard
+mappings, etc.
+
+<H2>Your Role</H2>
+
+I have some expectations as well. If you have a Wacom tablet and you
+use this code, I would appreciate it if you could test the beta releases
+whenever a feature or issue that might affect you comes up. The sooner
+that problems are resolved, the less likely you will be impacted by a
+buggy production release. If you only use the stable builds, none of
+the new features will be available to you until after they have been
+thoroughly hammered out which could be awhile. Of course, nothing is
+stopping you from using the stable builds; they are purposefully
+made available for people and companies who need the tablet drivers as
+stable as possible.
+<A NAME="updates">
+<H2>1.2 - Most Recent Updates</H2>
+
+<P>This is a list of the updates for the past month or so. The <A HREF="#changelog">Changelog</A> has a more complete
+list.
+
+<P>December 22, 2002 - Added support for the serial Intuos2 under xf86Wacom.c.
+Fixed the sign convention on the mouse wheel for USB Intuos mice. Graphire
+should be fixed, too. Added preliminary support for Volito to kernel driver.
+Cleaned up wacdump and hid-core to compile on Debian Woody without
+warnings. Added build help for Debian Stable users. Added ability to read
+serial devices using wacdump. New package is 0.3.3-beta.
+
+<P>December 15, 2002 - Added initial framework for new driver code. Fixed
+the mouse wheel for 4D mice. Fairly responsive, almost too responsive, in
+fact. (Updated: quick fix to xf86Wacom.c to remove some debugging code that
+got left in accidentally; new driver version is 0.3.2a, but the package is
+the same.)
+
+<P>December 13, 2002 - Added development page. New beta build has fix for
+2D mouse wheel. Wacdump displays relative data now. Build environment for
+wacom_drv.o set up for new code.
+
+<P>December 9, 2002 - Added more information about the parameters accepted
+by the InputDevice section, including the man pages.
+
+<P>November 24, 2002 - Moved beta tree to version 0.2.0. Added complete
+configure-based environment. wacom.o and wacdump are built by default;
+wacom_drv.o is available in binary and source form.
+
+<A NAME="success">
+<H2>1.3 - Success Stories</H2>
+<P>Originally, this document dealt only with Redhat 8.0. I will extend this
+to cover as many different distributions as possible, but I can personally
+only test on machines at my disposal. At present, that means Redhat 7.2 and
+8.0. If you have success stories for a distribution or version not mentioned,
+let me know so I can update the document.
+
+<UL>
+ <LI> Redhat
+ <UL>
+ <LI> Redhat 8.0 - tested on kernel 2.4.18-18.8.0
+ <LI> Redhat 8.0 - tested on kernel 2.4.18-17.8.0
+ <LI> Redhat 7.3 - tested on kernel 2.4.18-17.7.x
+ <LI> Redhat 7.2 - tested on kernel 2.4.18-17.7.x
+ </UL>
+ <LI> Mandrake
+ <UL>
+ <LI> Mandrake 9.0 - tested on ???
+ </UL>
+ <LI> Gentoo
+ <UL>
+ <LI> Gentoo 1.4rc - tested on 2.4.19 kernel, gcc3.2, XFree86 4.2.0
+ </UL>
+ <LI> Debian
+ <UL>
+ <LI>Debian Woody - XFree86 4.2 on 2.4.20
+ <LI>Debian Stable - XFree86 4.1 (see
+ <A HREF="#debwcmdrv">Building wacom_drv.o On Debian Stable</A>)
+ </UL>
+ <LI> Slackware
+ <UL>
+ <LI>Slackware 8.1, (standard release)
+ </UL>
+ <LI> SuSE
+ <UL>
+ <LI>Suse Linux 8.0, kernel 2.4.18, XFree 4.2
+ </UL>
+</UL>
+<A NAME="howto">
+<H2>1.4 - How To Use This Document</H2>
+<BLOCKQUOTE><DIV CLASS=diff>
+Where the guide differs between distributions or packages, I have added a
+comment like this to help distinguish between versions.
+</DIV></BLOCKQUOTE>
+
+<P>In terms of document organization, if you have not figured it out already,
+you can browse the document one page at a time, or you can click on the
+ALL link and view the entire thing in one long page.
+
+<P>Next, this document was written with the
+assumption that you are starting from
+scratch with a relatively recent distribution of the Linux kernel. Also,
+if you have already added lines to your X11 configuration file (XF86Config),
+you should comment them out and restart X. Since we'll be stepping through
+the entire process, we need X to ignore the tablet until we're ready.
+Otherwise, X will just get in the way.
+
+<P>Finally, if you know what you're doing, you can leave your X settings
+intact, print this out, switch to runlevel 3, and follow along from the
+console. The newer packages should have an HTML and text version of this
+document in the documentation directory. And of course, there's always
+<A HREF=http://lynx.isc.org/>lynx</A> for the gurus out there, which is
+why this document is written out in plain vanilla HTML.
+<A NAME="resources">
+<H2>1.5 - Resources and References</H2>
+
+<TABLE BORDER=0 CELLSPACING=5>
+<TR><TD VALIGN=TOP><A HREF="http://people.mandrakesoft.com/~flepied/projects/wacom/">Lepied's Wacom XInput driver for XFree86</A></TD><TD>This page contains the original driver and help for serial tablet users.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://www.lepied.com/archive_wacom.html">Lepied's Wacom Mailing List Archive</A></TD><TD>This is an archive of Lepied's driver mailing list.
+<I>Warning: posting directly to the mailing list page does not mail
+to the list. Many posts are missed this way.</I></TD></TR>
+<TR><TD VALIGN=TOP><A HREF="mailto:wacom-subscribe@lepied.com">Subscribe to Lepied's Wacom Mailing List</A></TD><TD>If you want to post to the list and receive notifications about driver updates, this link will subscribe you.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://www.runkel-it.de/wacom_tablet_howto.html">Stefan Runkel's Wacom Tablet HOWTO</A></TD><TD>This page is a bit old, but it covers a lot of details that I mention only briefly. Good reading, even if it is possibly outdated.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://www.wacom.com">Wacom Webpage</A></TD><TD>The Wacom web site contains little information about the Linux drivers, but you
+can look at their nifty new tablets.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://www.pxh.de/fs/graphire/">pxh: Graphire USB</A></TD><TD>This page is devoted to a Graphire-specific XFree86 driver.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://homepages.feis.herts.ac.uk/~bt7al/wcm/">A Webpage Dealing with Cintiq (Wacom PL-500 in particular) on Linux</A></TD><TD>This website deals with getting the Cintiq running; I will be merging this code into
+the kernel soon.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://sem.best.vwh.net/linux/wacom/">Wacom Intuos USB Micro-HOWTO</A></TD><TD>An older webpage which may not be relevent anymore,
+but was useful to me when I got started.</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="http://www.ecn.wfu.edu/~cottrell/thinkpad/tpa21m/wacom.html">Wacom Intuos USB on Linux</A></TD><TD>Another page that was useful to
+me when I started.</TD></TR>
+<TR><TD><A HREF="http://www.wacomeng.com/devsupport/index.html">Wacom Software Developer Support</A></TD><TD>This page contains some documentation for the
+older tablets.</TD></TR>
+</TABLE>
+<A NAME="faq">
+<H2>1.6 - Frequently Asked Questions</H2>
+
+<P>This FAQ is based on the questions that I receive in email. If you have
+a question that is not answered here, <A HREF="mailto:jej@j-arkadia.com?subject=WACOM">send me email directly.</A> Please add
+the word "WACOM" to the subject line to help me distinguish your message from
+the spam I normally receive.</P>
+
+<H2>Index</H2>
+<UL>
+<LI><A HREF=#WHO>Who is responsible for this code?</A>
+<LI><A HREF=#SUPPORT>Is my tablet supported? What about my special stylus?</A>
+<LI><A HREF=#OTHERS>What about my Penpartner/Graphire/Cintiq?</A>
+<LI><A HREF=#SPURIOUS>How do I get my stylus to stop clicking randomly whenever I use it?</A>
+<LI><A HREF=#FILTER>The old drivers had filtering, how do I enable it now?</A>
+<LI><A HREF=#FEATURE>What happened to feature XYZ?</A>
+<LI><A HREF=#BUSTED>How do I get the special features of my stylus to work?</A>
+</UL>
+
+<BR>
+<BR>
+<BR>
+
+<A NAME=WHO>
+<H3>Who is responsible for this code?</H3>
+
+<P>We are. That is, Linux users with Wacom tablets are responsible for
+the development of this code. The source code
+was written by at least two dozen individuals, none of whom still seem to
+be directly involved in the project. I, John Joganic, took up the code in
+November of 2002, largely to satisfy my own needs, but also because I believe
+Linux needs to be more useful in the graphic and video industries. What I
+ask of you, is to use the drivers and report the problems you encounter.
+
+<A NAME=SUPPORT>
+<H3>Is my tablet supported? What about my special stylus?</H3>
+
+<P>Most recent tablets are supported as well all the styluses. The
+<A HREF="#expect">Expectations</A> page has a
+list of all the tablets and styluses that are known to work as well
+as what you can expect out of these drivers now and in the future.
+
+<A NAME=OTHERS>
+<H3>What about my Penpartner/Graphire/Cintiq?</H3>
+
+The USB kernel driver currently seems to support Penpartner, Graphire,
+Graphire2, Intuos, Intuos2, and PL400-800. Based on user reports, the
+Cintiqs may identify themselves as PL series tablets.
+There is no kernel driver necessary for serial tablets.
+The XFree86 wacom driver supports both USB and serial devices. It has
+support for all the USB devices as well as virtually all of the older
+tablet models as well. Depending on how the Cintiq identifies itself,
+it may potentially work with the XFree86 driver as a serial device. There
+are no guarantees as of yet, so I would not buy one (currently over $3000
+USD) on the assumption that it will work without doing some actual research.
+
+<A NAME=SPURIOUS>
+<H3>How do I get my stylus to stop clicking randomly whenever I use it?</H3>
+
+<P>The "Threshold" setting in the InputDevice section of the XFree86
+configuration file determines the minimum pressure value before a touch
+event is detected. Wacdump can show you what pressure values are actually
+being received from the tablet, and setting the threshold to a value higher
+than that should solve the problem. My stylus oscillates between 0 and 6,
+even if the stylus is not touching the tablet. I use a threshold of 10.</P>
+
+<A NAME=FILTER>
+<H3>The old drivers had filtering, how do I enable it now?</H3>
+
+<P>At some point, the maintainers of the wacom kernel driver ripped out
+all the good features and moved them into the xf86Wacom driver. Not
+everything works as well as one might like. The "Suppress" setting in
+the InputDevice section specifies the tablet sensitivity.
+
+<A NAME=FEATURE>
+<H3>What happened to feature XYZ?</H3>
+
+<P>As mentioned above, many of the features in the original kernel driver
+were removed and transferred to the xf86Wacom driver. Not all the features
+made the trip. The goal of this website is to have all the original features
+working again as well as all the features available for the windows driver.
+If your favorite feature is not working, drop me a line. I prioritize my
+work based on my perception of user need.
+
+<A NAME=BUSTED>
+<H3>How do I get the special features of my stylus to work?</H3>
+
+<P>There are many different types of stylus, mice, and cursors. All of them
+work to varying degrees, so if you are running into trouble, send me email.
+There are presently known issues with the mouse wheel on 2D mice (fixed in
+0.3.1-beta), and some of the extra buttons on the 4D mice. While the values
+are generally reported correctly to the driver, the xf86Wacom driver does not
+interpret the information properly. The 4D mouse wheel for instance behaves
+completely contrary to expectation (fixed in 0.3.2-beta). These problems will
+be addressed in future production releases (and are available now in the beta
+releases).
+
+<P>I do not have an airbrush tool at my disposal. If you have one and it
+does not work for you, let me know. You could be beta-testing the code that
+gets this working.
+<A NAME="dev">
+<H2>1.7 - Development</H2>
+<H2>CURRENT DEVELOPMENT</H2>
+
+<TABLE BORDER=0>
+<TR><TD ALIGN=RIGHT>Date:&nbsp;&nbsp;</TD>
+<TD>2002-12-22 (Sunday)</TD></TR>
+<TR><TD ALIGN=RIGHT>Status:&nbsp;&nbsp;</TD>
+<TD>Moving over to SourceForge, and testing new serial driver.
+</TD></TR>
+<TR><TD ALIGN=RIGHT>Estimated Time:&nbsp;&nbsp;</TD>
+<TD>Ready on 2002-12-29 (Sunday)</TD></TR>
+</TABLE>
+
+<H3>Completed</H3>
+<UL>
+<LI>Added Intuos2 serial to xf86Wacom.c.
+<LI>Added serial tablet code to wacdump.c.
+<LI>Added experimental Volito code to kernel.
+<LI>Split ncurses and Linux input code to avoid namespace collision on Debian.
+<LI>Corrected wheel sign for USB mice.
+</UL>
+
+<H3>Immediate</H3>
+<UL>
+<LI>Get code onto SourceForge.
+<LI>Intuos2 2D mouse does not work correctly under xf86Wacom.c
+</UL>
+
+<H3>Secondary</H3>
+<UL>
+<LI>Getting test application for XF86 driver together
+<LI>Testing ability to create new pointer devices on the fly
+<LI>Testing ability to fork out new server process
+</UL>
+
+<BR><BR><BR>
+
+<H2>KNOWN ISSUES</H2>
+
+There are plenty of known issues. If you have one, and it is not listed,
+check the FAQ, and if it's not there either, let me know.
+
+<H3>wacom.o</H3>
+
+<UL>
+<LI>2002-12-13: Tablet does not reinitialize properly when replugged (report #1).
+</UL>
+
+<H3>wacdump</H3>
+
+<UL>
+<LI>Nothing outstanding.
+</UL>
+
+<H3>wacom_drv.o</H3>
+
+<UL>
+<LI>2002-12-22: Intuos2 2D mouse does not work correctly under xf86Wacom.c
+</UL>
+<A NAME="theory">
+<H2>1.8 - Wacom Driver Theory of Operation</H2>
+<P>Initially at least, the Wacom tablet is an HID compliant device, and when
+first connected to the computer, will identify itself as such.
+Unfortunately, this is not what you want because in this mode, you will not
+get any of the fancy features. The hid-core.c and usbmouse.c files contain
+exceptions for the wacom; when the device is detected, they ignore the
+tablet. In this way, the more sophisticated wacom driver
+has the opportunity to assume control.
+
+<P>The first thing that the driver does is register itself with the USB
+subsystem and wait for work to do. When the user plugs the device in, or
+the device is first detected, the USB subsystem shops the vendor and
+device identifier around, checking it against different drivers. The
+wacom driver takes responsibility for the tablet and then notifies the
+event system that it will be providing data. It then asks the tablet to
+switch from HID-compliant mode to "mode 2", a wacom-specific protocol which
+allows for values like pressure, Z rotation,
+and tilt. As information arrives, the wacom driver dutifully converts
+the data into real-world values and hands it on to the event system.
+
+<P>From here, any usermode application can get access to the event data
+by opening /dev/input/event0. A stream of events including mouse movements,
+clicks, and proximity updates can be read from the device. One such
+application that would be interested in this data is the X system itself.
+An XInput driver (wacom_drv.o) collects that information,
+applies various forms of filtering, and offers it to GUI-based applications
+throught the XInput API. A graphics program like gimp can then get access
+to the positions of various tools by querying X directly.
+
+<P>By breaking the responsibility for the data into three distinct levels,
+we keep the kernel code simple and robust, the applications generalized,
+and the fancy features commonly accessible to all GUI applications in the
+X window system itself. This document walks down the entire data path from
+the kernel driver to the gimp application.
+<A NAME="starthere">
+<H1>2.0 - Getting It Together</H1>
+<P>This section is devoted to preparing your system for the installation.
+In some cases, your distribution may have automatically loaded certain
+modules which you will now need to unload. Incidentally, some distributions
+detect new hardware on boot. If you allow Redhat's "anaconda" for instance
+to automatically configure (or remove) your tablet, it may undo some of the
+settings you will make here. Until Redhat's installation program recognizes
+Wacom tablets as non-HID devices by default, you are best off in my opinion
+to <i>not</i> allow it to configure the device. Just a warning.
+<A NAME="beforestart">
+<H2>2.1 - Before We Start</H2>
+<P>From the beginning, let's make certain that we are on the
+same page. First, unplug the USB Wacom. If you have Wacom related lines
+in your XF86Config file, you should comment them out or remove them; then
+restart X.<BR>
+
+<P>Unload the old modules.
+<BLOCKQUOTE><PRE>
+[jej@ayukawa jej]$ su -
+[root@ayukawa root]# lsmod | grep wacom
+<B>wacom</B> 8020 0 (unused)
+input 5920 0 [<B>evdev</B> <B>wacom</B> mousedev keybdev hid]
+usbcore 77024 1 [<B>wacom</B> usb-storage snd printer hid usb-uhci]
+[root@ayukawa root]# rmmod wacom evdev
+</PRE></BLOCKQUOTE>
+
+The kernel now knows nothing about the Wacom or how to handle event-based
+input devices. If you get an error unloading (busy driver perhaps), try
+rebooting.<BR>
+
+<P>Check the presence of the input devices in the /dev/input directory.
+You should find approximately 114 devices with names such as event*, mouse*,
+keyboard, js*, and ttyACM*. We are interested in two devices, mouse0 and
+event0.
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa root]# cd /dev/input
+[root@ayukawa input]# ls -la mouse0 event0
+crw------- 1 root root 13, 64 Aug 30 16:31 event0
+crw------- 1 root root 13, 32 Aug 30 16:31 mouse0
+[root@ayukawa input]# xxd mouse0
+xxd: mouse0: No such device
+[root@ayukawa input]# xxd event0
+xxd: event0: No such device
+</PRE></BLOCKQUOTE>
+
+<P>On some distributions, Mandrake 9.0 for instance, the /dev directory is
+managed by devfs; Redhat 8.0 does not use it. When devfs is running, the
+/dev/input directory and/or its contents may be missing if the driver is not
+loaded. If you are not using devfs and the /dev/input directory or
+mouse0/event0 devices are not present, your distribution may be lacking these
+devices. This could be an oversight, or it could be an indication that your
+distribution is too old.
+
+<P>If after executing <i>xxd mouse0</i> you <B>did not</B>
+receive the "xxd: mouse0: No such device" message, then the input device
+is still loaded; go back and try running <i>rmmod evdev</i> again.
+Once the new drivers are compiled and installed, this command will stream
+information from the Wacom tablet. mouse0 behaves like a PS/2 style mouse.
+event0 provides extensive information about the tablet including position,
+tilt, pressure, and buttons. It can also track at least two different tools
+simultaneously (Intuos2).<BR>
+<A NAME="download">
+<H2>2.2 - Downloading the Code</H2>
+The file <A HREF="/wacom/wacpack-0.2.0.tar.gz">wacpack-0.2.0.tar.gz</A> is the stable package and contains
+files that you will need to get the tablet working. The current beta
+package <A HREF="/wacom/wacpack-0.3.3-beta.tar.gz">wacpack-0.3.3-beta.tar.gz</A> is also available and may be
+used by people who are willing to risk an occasional kernel panic to help
+test new features. I will never put a beta package on this site that I am
+not running myself on my primary development machine, so you can be certain
+that if there are any obvious show stoppers, they will be fixed before you
+get to see them.
+
+
+<H3>Stable files included for wacpack-0.2.0:</H3>
+<TABLE BORDER=0 CELLSPACING=5>
+<TR><TH ALIGN=LEFT>File</TH><TH ALIGN=LEFT>Comment</TH></TR>
+<TR><TD VALIGN=TOP>configure</TD><TD>- configure script for distribution independent builds</TD></TR>
+<TR><TD VALIGN=TOP>prebuilt/wacom_drv.o</TD><TD>- binary X11 driver for the wacom (26-j0.2.0)</TD></TR>
+<TR><TD VALIGN=TOP>src/wacom.c</TD><TD>- a stable, working kernel driver (1.30-j0.2.0)</TD></TR>
+<TR><TD VALIGN=TOP>src/wacdump.c</TD><TD>- a simple program for displaying tablet event data directly using ncurses; current version is 0.2.0</TD></TR>
+<TR><TD VALIGN=TOP>src/usbmouse.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/hid-core.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/hiddev.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/hid-input.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/xf86Wacom.c</TD><TD>- source for wacom_drv.o; requires XF86 build environment to compile. Binary available in prebuilt directory.</TD></TR>
+<TR><TD VALIGN=TOP>GPL</TD><TD>- the GNU General Public License, in case you did not already have twenty or more lying around</TD></TR>
+</TABLE>
+
+<H3>Beta files included for wacpack-0.3.2-beta:</H3>
+<TABLE BORDER=0 CELLSPACING=5>
+<TR><TH ALIGN=LEFT>File</TH><TH ALIGN=LEFT>Comment</TH></TR>
+<TR><TD VALIGN=TOP>configure</TD><TD>- configure script for distribution independent builds</TD></TR>
+<TR><TD VALIGN=TOP>prebuilt/wacom_drv.o</TD><TD>- binary X11 driver for the wacom (26-j0.3.2a)</TD></TR>
+<TR><TD VALIGN=TOP>src/wacom.c</TD><TD>- a stable, working kernel driver (1.30-j0.3.1)</TD></TR>
+<TR><TD VALIGN=TOP>src/wacdump.c</TD><TD>- a simple program for displaying tablet event data directly using ncurses; current version is 0.3.2</TD></TR>
+<TR><TD VALIGN=TOP>src/usbmouse.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/hid-core.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/hiddev.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/hid-input.c</TD><TD>- replacement kernel driver (2.4.19), use only if needed</TD></TR>
+<TR><TD VALIGN=TOP>src/xf86Wacom.c</TD><TD>- legacy source for wacom_drv.o; requires XF86 build environment to compile.</TD></TR>
+<TR><TD VALIGN=TOP>src/wcm-beta.c</TD><TD>- beta source for wacom_drv.o; requires XF86 build environment to compile.</TD></TR>
+<TR><TD VALIGN=TOP>src/wcm-beta.h</TD><TD>- beta source for wacom_drv.o; requires XF86 build environment to compile.</TD></TR>
+<TR><TD VALIGN=TOP>GPL</TD><TD>- the GNU General Public License, in case you did not already have twenty or more lying around</TD></TR>
+</TABLE>
+
+<H3>Stable Packages by Version and Date:</H3>
+<TABLE BORDER=0 CELLSPACING=5>
+<TR><TH ALIGN=LEFT>File</TH><TH ALIGN=LEFT>Date</TH><TH ALIGN=LEFT>Comment</TH></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.2.0.tar.gz">wacpack-0.2.0.tar.gz</A></TD><TD> - 2002-11-24</TD><TD>Configure-based package</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1b.tar.gz">wacpack-0.1b.tar.gz</A></TD><TD> - 2002-11-06</TD><TD></TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1a.tar.gz">wacpack-0.1a.tar.gz</A></TD><TD> - 2002-11-05</TD><TD></TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1.tar.gz">wacpack-0.1.tar.gz</A></TD><TD> - 2002-11-04</TD><TD></TD></TR>
+</TABLE>
+<H3>Beta Packages by Version and Date:</H3>
+<TABLE BORDER=0 CELLSPACING=5>
+<TR><TH ALIGN=LEFT>File</TH><TH ALIGN=LEFT>Date</TH><TH ALIGN=LEFT>Comment</TH></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.3.3-beta.tar.gz">wacpack-0.3.3-beta.tar.gz</A></TD><TD> - 2002-12-22</TD><TD>Serial Intuos2, serial wacdump, Volito added, USB mouse wheel direction fixed, kernel 2.4.20 friendly</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.3.2-beta.tar.gz">wacpack-0.3.2-beta.tar.gz</A></TD><TD> - 2002-12-15</TD><TD>Fix for 4D mice and working beta driver shell</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.3.1-beta.tar.gz">wacpack-0.3.1-beta.tar.gz</A></TD><TD> - 2002-12-13</TD><TD>Fix for 2D mice and relative events in wacdump</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.3.0-beta.tar.gz">wacpack-0.3.0-beta.tar.gz</A></TD><TD> - 2002-11-24</TD><TD>Configure-based package</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1g-beta.tar.gz">wacpack-0.1g-beta.tar.gz</A></TD><TD> - 2002-11-14</TD><TD>wacdump updated; displays ranges, cleaner output</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1f-beta.tar.gz">wacpack-0.1f-beta.tar.gz</A></TD><TD> - 2002-11-14</TD><TD>build environment for xf86Wacom added</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1e-beta.tar.gz">wacpack-0.1e-beta.tar.gz</A></TD><TD> - 2002-11-11</TD><TD>fixed range for I2-6x8</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1d-beta.tar.gz">wacpack-0.1d-beta.tar.gz</A></TD><TD> - 2002-11-10</TD><TD>updated wacdump, fixed range for I2-12x12</TD></TR>
+<TR><TD VALIGN=TOP><A HREF="/wacom/wacpack-0.1c-beta.tar.gz">wacpack-0.1c-beta.tar.gz</A></TD><TD> - 2002-11-09</TD><TD>updates to range, pressure, and mouse events</TD></TR>
+</TABLE>
+<A NAME="config">
+<H2>2.3 - Configuring the Package</H2>
+
+Versions of wacpack-0.2.0 and greater are based on GNU's configure script
+based build environment. The upside is that more details are guessed by
+default. The downside is that the default configuration builds only the
+kernel driver and wacdump programs (which should cover almost everybody),
+but if you need a different module built, you will have to reconfigure the
+package first. This is mostly because different modules have different
+dependencies, some of which are difficult to resolve.
+A list of the options currently available in the stable package is
+presented below. Remember that for every --enable option, there is also
+an equivalent --disable option.
+
+<BR><BR>
+
+<TABLE BORDER=0 CELLSPACING=5>
+<TR><TH ALIGN=LEFT>Option</TH><TH ALIGN=LEFT>Default</TH>
+ <TH ALIGN=LEFT>Builds</TH></TR>
+<TR><TD>--enable-wacom</TD><TD><B>yes</B></TD>
+ <TD>wacom.o kernel driver</TD></TR>
+<TR><TD>--enable-wacdump</TD><TD><B>yes</B></TD>
+ <TD>wacdump event monitor</TD></TR>
+<TR><TD>--enable-hid</TD><TD>no</TD>
+ <TD>hid.o replacement kernel driver (not normally necessary)</TD></TR>
+<TR><TD>--enable-usbmouse</TD><TD>no</TD>
+ <TD>usbmouse.o replacement kernel driver (not normally necessary)</TD></TR>
+<TR><TD>--enable-wacomdrv</TD><TD>no</TD>
+ <TD>wacom_drv.o XFree86 driver (binary is available in prebuilt directory)</TD></TR>
+<TR><TD>--enable-modver</TD><TD>best guess</TD>
+ <TD>enables kernel module versioning; usually guesses correctly, but
+ can be enabled or disabled if not</TD></TR>
+<TR><TD>--with-kernel=dir</TD><TD>best guess</TD>
+ <TD>Specifies the kernel source directory if configure cannot guess correctly.</TD></TR>
+<TR><TD>--with-xf86=dir</TD><TD>none</TD>
+ <TD>Specifies the XFree86 build directory.</TD></TR>
+<TR><TD>--with-arch=arch</TD><TD>best guess</TD>
+ <TD>Specifies the architecture if configure guesses incorrectly.</TD></TR>
+</TABLE>
+
+<P>Chances are high that you will be able to run the configure script without
+any options and have it execute correctly. Here is a sample output of the
+script on a Redhat 8.0 system:
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacpack]$ ./configure
+checking for a BSD-compatible install... /usr/bin/install -c
+checking whether build environment is sane... yes
+...
+checking for processor type... <B>i686</B>
+checking for kernel module versioning... <B>yes</B>
+checking for kernel sources... <B>/usr/src/linux-2.4</B>
+...
+
+----------------------------------------
+ BUILD ENVIRONMENT:
+ architecture - i686
+ module versioning - yes
+ kernel - yes
+ XFree86 - no
+
+ BUILD OPTIONS:
+ wacom.o - yes
+ wacdump - yes
+ hid.o - no
+ usbmouse.o - no
+ wacom_drv.o - no
+----------------------------------------
+</PRE></BLOCKQUOTE>
+
+<P>The processor type is determined by the script and used to build the
+kernel modules. Similarily, the script attempts to discover if the kernel
+is using module versioning by looking for version numbers in the usb.o
+module of the currently active kernel. This may not work well on all
+distributions, so if you get a line like "checking for kernel module versioning... confused" or "maybe; assuming yes", you'll have to manually enable or
+disable the option if it guesses incorrectly.
+
+<P>The kernel source directory is assumed to be either /usr/src/linux-2.4
+or /usr/src/linux. If your kernel sources are elsewhere, you will need to
+specify the directory with the --with-kernel option.
+
+<P>Generally, you will not need to build wacom_drv.o since it ships in
+binary form in the prebuilt directory. It is compiled with glibc with
+gcc 3.2 on a Redhat 8.0 system; if this will not work for you, building
+from source may be your only option. See the
+<A HREF="#builddrv">Building wacom_drv.o From Scratch</A> page
+for more information.
+
+<P>The configure script is pretty simple, so if you find any problems and
+are inclined to fix them, please send me your patch so I can include the
+change in future releases.
+
+<P>The following sample command-line will build everything but wacdump
+while disabling module versioning:
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacpack]$ ./configure --enable-hid --enable-usbmouse \
+ --enable-wacomdrv --with-xf86=/usr/src/redhat/BUILD/XFree86-4.2.0 \
+ --disable-modver --disable-wacdump
+checking for a BSD-compatible install... /usr/bin/install -c
+checking whether build environment is sane... yes
+...
+checking for processor type... i686
+checking for kernel module versioning... <B>yes</B>
+checking for kernel sources... /usr/src/linux-2.4
+checking for valid XFree86 build environment... <B>ok</B>
+...
+----------------------------------------
+ BUILD ENVIRONMENT:
+ architecture - i686
+ module versioning - <B>no</B>
+ kernel - yes
+ XFree86 - <B>yes</B>
+
+ BUILD OPTIONS:
+ wacom.o - yes
+ wacdump - <B>no</B>
+ hid.o - <B>yes</B>
+ usbmouse.o - <B>yes</B>
+ wacom_drv.o - <B>yes</B>
+----------------------------------------
+</PRE></BLOCKQUOTE>
+
+<P>Notice that the configure script guessed module versioning was enabled
+by default, but was disabled by the command-line option --disable-modver.
+Similarly, the wacdump program which is enabled by default was also disabled.
+All the kernel modules and the XFree86 wacom driver are enabled.
+
+<P>If the configure script fails to find something that it is looking for,
+it may disable some options that you previously enabled on the command-line.
+If this happens, check the output for a warning like the following:
+
+<BLOCKQUOTE><PRE>
+***
+*** WARNING:
+*** Unable to compile wacom_drv.o without XF86 build environment
+*** wacom_drv.o will not be built
+***
+</PRE></BLOCKQUOTE>
+
+<P>In this particular case, the XFree86 driver was enabled, but the
+--with-xf86 option was not specified. Without the build environment,
+the module cannot be compiled and was consequently disabled.
+<A NAME="kernel">
+<H1>3.0 - The Kernel Driver</H1>
+<P>The kernel driver for the Wacom Intuos that ships with the
+Linux kernel through 2.4.19, and in particular Redhat 8.0, does
+not work correctly. The bug is pretty simple and you can fix it yourself.
+Optionally, you can compile the version that I provide directly and swap
+the new module for the old one. This avoids having to rebuild the entire
+kernel.</P>
+
+<P>Kernel modules must be recompiled for each new kernel so I can't
+just provide binaries. By the time you read this, my present kernel
+will be entirely out of date with yours. Additionally, this fix will
+appear in 2.4.20, so until you upgrade your kernel to that point, each
+time you upgrade to a version less than that, you will need to recompile this
+driver and swap it for the old one. In many cases, distributions like
+Redhat will "back-port" the fixes to older kernels, so keep an eye out
+for the change. Your driver may already be fixed.</P>
+<A NAME="newwacom">
+<H2>3.1 - Updated wacom.c</H2>
+The wacom.c driver that is available in <A HREF="/wacom/wacpack-0.2.0.tar.gz">wacpack-0.2.0.tar.gz</A> has a
+number of updates which cannot be found in many distributions yet.
+
+<P>First and foremost, it fixes an error that shipped with
+the 2.4.19 kernel and was subsequently fixed in 2.4.20. This bug fix was
+made available in the Redhat 8.0 back-ported kernel version 2.4.18-18.x.y.
+Other distributions may not yet be updated.
+
+<P>This driver contains several changes provided by Wacom UNIX developer
+Ping Cheng. Items include revised pressure code, better handling of 2D
+mice and lens cursors.
+
+<P>The driver also contains additional debugging code which may be useful in
+diagnosing data flow problems of which there are many, unfortunately.
+<A NAME="buildwacom">
+<H2>3.2 - Building wacom.c</H2>
+<P>Building the kernel driver requires having the kernel source. If you
+are running on Redhat, you can get it by installing the kernel-source RPM.
+At the time of this writing, I have the following RPMs installed:
+
+<BLOCKQUOTE>
+kernel-2.4.18-18.8.0<BR>
+kernel-2.4.18-14<BR>
+kernel-source-2.4.18-18.8.0<BR>
+</BLOCKQUOTE>
+
+On a Redhat 8.0 machine, the kernel source is installed to /usr/src/linux-2.4. You
+will need to know where your source is installed to continue with the next
+step. After unpacking the <A HREF="/wacom/wacpack-0.2.0.tar.gz">wacpack-0.2.0.tar.gz</A> file, configure the
+package. If your kernel is not located at either /usr/src/linux-2.4 or
+/usr/src/linux, use the --with-kernel option as shown below. Otherwise,
+just run <I>configure</I> without any options.
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacpack]$ ./configure --with-kernel=/home/jej/src/linux
+...
+checking for valid kernel source tree... <B>ok</B>
+...
+----------------------------------------
+ BUILD ENVIRONMENT:
+ architecture - i686
+ module versioning - yes
+ kernel - <B>yes</B>
+ XFree86 - no
+
+ BUILD OPTIONS:
+ wacom.o - yes
+ wacdump - yes
+ hid.o - no
+ usbmouse.o - no
+ wacom_drv.o - no
+----------------------------------------
+</PRE></BLOCKQUOTE>
+
+<P>If you did not receive an error or warning and the BUILD ENVIRONMENT
+summary shows "kernel - yes" then your kernel source tree was detected.
+If not, then you will need to look at the error or warning and determine
+the correct source directory. Now is also a good time to verify that the
+architecture was correctly detected. The --with-arch option allows you
+to change the value.
+
+<P>Another detail that you need to be concerned about is module
+versioning. If it reads "yes" or "no" then the configure script
+has determined the correct setting. If it says "unknown," then it will
+default disabled, and you may have to enable it manually with --enable-modver.
+If module versioning is disable when it should be enabled, depmod will
+complain about missing symbols. If it is enabled when it should be disabled,
+the code may not compile, and it almost certainly will not load properly.
+If in doubt, leave it disabled for now and enable it later if depmod complains.
+
+<P>To build the driver, just run <i>make</i>. The
+output will be a file called wacom.o in the src directory.
+This is your replacement driver.
+<A NAME="testwacom">
+<H2>3.3 - Testing If wacom.o Will Load</H2>
+<P>Before we install the wacom driver, we need to test that it will load
+properly. We do this by loading the driver manually. WARNING: there is a
+small chance that this will bomb your kernel, so we run <i>sync</i> to write
+all the stale buffers to the disk. People using ext3 have little to worry
+about, but it's always good to be prepared for the worst. At the very least,
+save your work.
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacom]# su -
+[root@ayukawa root]# cd /home/jej/src/wacom/src
+[root@ayukawa src]# sync
+[root@ayukawa src]# rmmod wacom
+[root@ayukawa src]# insmod wacom.o # for those about to rock, we salute you.
+</PRE></BLOCKQUOTE>
+
+<P>Well, if you did not bomb, then good. And if you did, well, sorry. So
+far, we have not had any reports of this happening, so please send in yours.
+
+<P>If you get errors inserting the module, then you may need to reconfigure
+and build with module versioning disabled. If it loads without a hitch,
+move on to the next part.
+<A NAME="installwacom">
+<H2>3.4 - Installing wacom.o</H2>
+<P>To install or not to install, that is the question.
+Since the driver is in memory, you can pretty much use it this way
+throughout the rest of this document. Anywhere you see
+<i>modprobe wacom</i>, you'll instead need to <i>insmod wacom.o</i>.
+You'll also need to be careful that you use the correct path to wacom.o
+since insmod will load the driver from the kernel modules directory if it
+fails to find the module you specified. The result is that you'll
+be using the wrong driver.
+
+<P>Why would you not install the driver? Well, for one, you may be using
+a beta driver, and if the system crashes (you get an Oops or things come
+unglued in other ways), it would be nice to reboot and have the original
+drivers load instead.
+
+<P>When should I install the driver? When you're comfortable that the
+driver will not crash your system. The wacpack tarballs will be marked
+as beta if I am not absolutely convinced that they are stable. On the
+other hand, the new features will be in beta tarballs before they are
+marked stable, so there you have it. For the first time through this document,
+I would recommend installing the files found in the stable tarball. If you
+really know what you're doing, just load the drivers manually like in the
+previous section <A HREF="#testwacom">Testing If wacom.o Will Load</A>.
+
+<P CLASS=diff>On some distributions, Mandrake included, the wacom.o driver
+that appears in the kernel modules directory appears to be compressed. If
+you cannot find wacom.o using the method below, try locating wacom.o.gz
+instead. People who encountered this problem were able to run gzip on the
+module and copy that instead.</P>
+
+<P>Installing the driver requires knowing where it belongs. A
+little research will help here. By using the <i>locate</i> command, you
+can find all copies of the original driver on the computer.</P>
+
+<BLOCKQUOTE><PRE>
+jej@ayukawa wacom]$ locate wacom.o
+/lib/modules/2.4.18-14/kernel/drivers/usb/<B>wacom.o</B>
+/lib/modules/2.4.18-18.8.0/kernel/drivers/usb/<B>wacom.o</B>
+
+[jej@ayukawa wacom]$ uname -r
+<B>2.4.18-18.8.0</B>
+</PRE></BLOCKQUOTE>
+
+On this computer, there are two kernels installed. <I>uname</i> identifies
+the currently active kernel as 2.4.18-18.8.0. The correct driver to replace
+is therefore at /lib/modules/2.4.18-18.8.0/kernel/drivers/usb/wacom.o. You
+will need to be root to replace this file, and it is a very good idea to
+make a backup copy.
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacom]$ su -
+[jej@ayukawa root]# cd /lib/modules/2.4.18-18.8.0/kernel/drivers/usb
+[jej@ayukawa usb]# cp <B>wacom.o</B> /home/jej/src/wacom/<B>wacom_old.o</B>
+[jej@ayukawa usb]# cp /home/jej/src/wacom/<B>wacom.o</B> <B>wacom.o</B>
+</PRE></BLOCKQUOTE>
+
+Here, I've saved the original to wacom_old.o and copied my new driver over it.
+Substitute directory names as appropriate.
+
+<P>Finally, it is always a good thing to update the module dependencies.
+This is where you find out if the module was compiled without kernel module
+versioning. The following command, even if it generates errors is relatively
+benign. If it fails, then there is no harm done. It just means that you will
+have to load modules in the correct order since the system will not be able to
+guess for you.
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa usb]# depmod -e
+</PRE></BLOCKQUOTE>
+
+If you get no errors and no output, everything is fine, and the module was
+compiled, linked, and installed properly. If you received unresolved symbols
+like usb_set_idle or printk, then you need to reconfigure with module
+versioning enabled and recompile. Or not. As I said, it is not critical.
+<A NAME="loadwacom">
+<H2>3.5 - Loading the wacom Driver</H2>
+
+<P>If you have installed the driver, now is the time to test whether it will
+load when needed. If you have not installed it, but are instead using insmod,
+substitute <i>insmod mydir/wacom/wacom.o</i> where you see
+<i>modprobe wacom</i> below. It is important that you use the correct
+wacom.o file, the one you just built, since insmod may load the old driver
+if it cannot find the one you have specified.
+
+<P>I am assuming
+that you are using a USB device, so you will also need to modprobe for usb-uhci.
+The important features of the stylus are available through the Linux event
+drivers, so load those too. With some clever additions to /etc/modules.conf,
+you can probably have these automatically loading for you whenever the tablet
+is activated. So far, I haven't figured out how to make this work reliably,
+so I've added the commands to my /etc/rc.d/rc.local file.
+
+<BLOCKQUOTE><DIV CLASS=diff>
+On Redhat 7.2 and possibly on other older distributions, mousedev and input
+are not loaded by default. Without them, the wacom driver may fail to load,
+due to unresolved symbols, and the mouse driver may not receive wacom mouse
+events, even if loaded afterwards.
+</DIV></BLOCKQUOTE>
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa usb]# rmmod wacom
+[jej@ayukawa usb]# modprobe usb-uhci
+<SPAN CLASS=diff>[jej@ayukawa usb]# modprobe input</SPAN>
+<SPAN CLASS=diff>[jej@ayukawa usb]# modprobe mousedev</SPAN>
+[jej@ayukawa usb]# modprobe wacom
+[jej@ayukawa usb]# modprobe evdev
+</PRE></BLOCKQUOTE>
+
+<P>Check the system log for status messages pertaining to the wacom.
+Here's a copy of the messages from my version of the driver.
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa usb]# grep -i wacom /var/log/messages | tail
+Dec 22 21:23:35 ayukawa kernel: usb.c: registered new driver wacom
+Dec 22 21:23:35 ayukawa kernel: wacom.c: <B>v1.30-j0.2.0</B> Vojtech Pavlik &lt;vojtech@suse.cz&gt;
+Dec 22 21:23:35 ayukawa kernel: wacom.c: USB Wacom Graphire and Wacom Intuos tablet driver (MODIFIED)
+</PRE></BLOCKQUOTE>
+
+<P>The original driver was version 1.21.3. This version number
+is 1.30-j0.2.0 so the correct driver was loaded.</P>
+<A NAME="testtablet">
+<H2>3.6 - Testing Tablet Detection</H2>
+<P>In this section we will plug the tablet into the computer and determine
+which driver, if any, claims control over the tablet. There are at least
+three drivers that are interested: 1) hid.o which may think it is an HID
+device, 2) usbmouse.o which may think it is an HID mouse, and 3) the wacom
+driver which should identify the tablet as its own. Any number of problems
+may be experienced here, so be prepared to spend some time looking at the
+logs. A good way to do this, if you are running X, is to open a separate
+xterm, su to root, and run <i>tail -f /var/log/messages</i>. The console
+window will stream anything that is appended to the log.
+<A NAME="plugin">
+<H3>3.6.1 - Plug It In</H3>
+
+<P>Test the tablet detection by plugging the wacom into the USB port and
+checking /var/log/messages again. You should see a flurry of activity. The
+exact output depends a lot on your particular kernel and distribution.
+
+<BLOCKQUOTE><PRE>
+This is Redhat 8.0 (2.4.18-17.8.0):<BR>
+[jej@ayukawa usb]# tail /var/log/messages
+Dec 22 21:26:11 ayukawa kernel: hub.c: USB new device connect on bus2/2, assigned device number 2
+Dec 22 21:26:11 ayukawa kernel: <B>input0: Wacom Intuos2 12x12</B> on usb2:2.0
+Dec 22 21:26:14 ayukawa /etc/hotplug/usb.agent: Setup wacom hid for USB product 56a/44/115
+Dec 22 21:26:14 ayukawa /etc/hotplug/usb.agent: Setup <B>mousedev</B> for USB product 56a/44/115
+</PRE>
+<PRE CLASS=diff>
+And here it is again on Redhat 7.2 (2.4.18-17.7.x):<BR>
+[jej@sasami root]# tail /var/log/messages
+Dec 22 21:28:38 sasami kernel: hub.c: USB new device connect on bus1/1, assigned device number 2
+Dec 22 21:28:38 sasami kernel: <B>input0: Wacom Intuos2 12x12</B> on usb1:2.0
+Dec 22 21:28:39 sasami kernel: usb.c: registered new driver hiddev
+Dec 22 21:28:39 sasami kernel: usb.c: registered new driver hid
+Dec 22 21:28:39 sasami kernel: hid-core.c: v1.8.1 Andreas Gal, Vojtech Pavlik &lt;vojtech@suse.cz&gt;
+Dec 22 21:28:39 sasami kernel: hid-core.c: USB HID support drivers
+Dec 22 21:28:39 sasami kernel: mice: <B>PS/2 mouse</B> device common for all mice
+</PRE>
+</BLOCKQUOTE>
+
+<P>If all went well like above, the USB device was successfully detected and
+handled by the wacom driver; secondly, it was assigned input0. This
+presumably means that information like pressure and tilt will be received on
+/dev/input/event0. Lastly, the tablet was setup with mousedev or the generic
+PS/2 USB mouse driver, so that information should appear on /dev/input/mouse0.
+
+<P>If instead you got any of the following lines in your log, the wacom
+driver <i>did not get control</i>. Either hid or usbmouse did.
+
+<BLOCKQUOTE><PRE>
+ <B>input0,hiddev0: USB HID v1.00 Mouse</B> [Tablet XD-0608-U] on usb1:5.0
+ <B>input0: Tablet XD-1212-U</B> on usb2:2.0
+<PRE></BLOCKQUOTE>
+
+<P>The next section describes what to do if the wacom driver did not get
+control. Otherwise, skip on to
+<A HREF="#viewdata">Viewing the Raw Data</A>.
+
+<A NAME="noctrl">
+<H3>3.6.2 - Wacom Driver Does Not Get Control</H3>
+
+<P>If the wacom driver did not get control, and instead you see
+"Tablet XD-1212-U" or similar in its place, then this section is for
+you. Otherwise, consider yourself lucky and skip on to the next section,
+<A HREF="#viewdata">Viewing the Raw Data</A>. For the unlucky,
+there is a good possibility that your kernel is slightly out of date and
+that your usbmouse.c and hid-core.c files need some help.
+
+<P>When the Tablet XD-1212-U driver comes up, either usbmouse.o or hid.o
+has discovered your tablet, decided it was an HID device, and grabbed it.
+This is bad news because HID tablets seem to only output relative
+coordinates and nothing about pressure. You are in good company though
+because a number of people have reported this, even on some fairly
+up-to-date distributions. Now let's fix it.
+
+
+<A NAME="buildhid">
+<H3>3.6.3 - Building usbmouse.o and hid.o</H3>
+
+<P>In the <A HREF="/wacom/wacpack-0.2.0.tar.gz">wacpack-0.2.0.tar.gz</A> file, you will find the 2.4.19 versions
+of usbmouse.c and hid-core.c files which have special exceptions for wacom.
+These files are not built by default, so you will need to reconfigure the
+package and run make again.
+
+<DIV CLASS=diff>
+At least one person has reported errors building these files on Debian Woody
+running kernel version 2.4.20. This is not surprising since the
+source code came from 2.4.19, but I will do what I can to get these drivers updated in the near future.
+</DIV>
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacom]$ ./configure --enable-hid --enable-usbmouse
+...
+ BUILD OPTIONS:
+ hid.o - <B>yes</B>
+ usbmouse.o - <B>yes</B>
+...
+[jej@ayukawa wacom]$ make
+[jej@ayukawa wacom]$ cd src
+[jej@ayukawa src]$ ls -la hid.o usbmouse.o
+-rw-rw-r-- 1 jej jej 26590 Dec 22 18:56 hid.o
+-rw-rw-r-- 1 jej jej 4848 Dec 22 18:56 usbmouse.o
+</PRE></BLOCKQUOTE>
+
+<P> First off, if the code does not build, and you cannot chase the problem
+down yourself, send me a line. If your kernel is much older than 2.4.18,
+then you may have to resign yourself to the fact that the 2.4.19 drivers
+are not going to work for you. I would recommend upgrading your kernel. Some
+people are already running kernel version 2.4.20 and have encountered
+problems building as well. I will update these drivers to 2.4.20 as soon as possible.
+
+<P>Rather than copy these files to the kernel modules directory right
+away, it would be wise to test whether they get the job done. We should also
+verify that they load properly.
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa src]$ su -
+[root@ayukawa root]# cd /home/jej/src/wacom/src
+[root@ayukawa src]# rmmod hid usbmouse wacom
+[root@ayukawa src]# insmod hid.o
+[root@ayukawa src]# insmod usbmouse.o
+[root@ayukawa src]# insmod wacom.o
+</PRE></BLOCKQUOTE>
+
+As for unloading the old drivers, none of them should be busy, but if you
+run into trouble, you can use <i>lsmod</i> to check the dependencies
+and try again. Now let's check how the wacom driver managed:
+
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacom]# tail -15 /var/log/messages
+Dec 22 21:07:38 ayukawa kernel: usbmouse.c: <B>mouse_init (MODIFIED DEBUG)</B>
+Dec 22 21:07:38 ayukawa kernel: usb.c: registered new driver usb_mouse
+Dec 22 21:07:39 ayukawa kernel: hid-core.c: <B>hid_init (MODIFIED DEBUG)</B>
+Dec 22 21:07:39 ayukawa kernel: usb.c: registered new driver hiddev
+Dec 22 21:07:39 ayukawa kernel: usb.c: registered new driver hid
+Dec 22 21:07:44 ayukawa kernel: usb.c: registered new driver wacom
+Dec 22 21:07:44 ayukawa kernel: usbmouse.c: <B>usb_mouse_probe: ignoring wacom</B>
+Dec 22 21:07:44 ayukawa kernel: input0: <B>Wacom Intuos2 12x12</B> on usb2:8.0
+Dec 22 21:07:44 ayukawa kernel: wacom.c: v1.30-j0.2.0 Vojtech Pavlik &lt;vojtech@suse.cz&gt;
+Dec 22 21:07:44 ayukawa kernel: wacom.c: USB Wacom Graphire and Wacom Intuos tablet driver <B>(MODIFIED-DEBUG)</B>
+</PRE></BLOCKQUOTE>
+<P>If everything is working correctly, the log should look similar to the
+lines above. I removed some of the extraneous information, so if you have
+more than this, you should be fine. The most important thing here is that
+the usb_mouse_probe detected and ignored the wacom. It actually gets
+several shots at it, but I removed the redundant lines. For the final test,
+unplug and replug the tablet. Now check the log. The wacom driver should
+be calling the shots from here on out.
+
+<P>If all is well, and you are installing modules rather than <i>insmod</i>'ing
+them as you go, then now is the time to copy the usbmouse.o and hid.o
+drivers to their proper places in the kernel modules directory.
+<SPAN CLASS=diff>As before with some other distributions, if the files in the
+kernel module directory are compressed, you'll need to run gzip on the .o
+files to get .o.gz files.</SPAN>
+Use <i>locate</i> as you did for the wacom.o file if necessary and don't
+forget to backup the originals.
+<A NAME="morehid">
+<H3>3.6.4 - More about hid.o</H3>
+
+<P>Incidentally, the hid.o file may not be necessary, but I've included the
+source to build it anyway because hid-core.c contains a lot of wacom related
+code. If the hid.o driver gets called upon to control the wacom, and it is
+compiled in debug mode, you may see a line similar to the following in the
+log:
+
+<BLOCKQUOTE><PRE>
+Dec 22 21:11:44 ayukawa kernel: usb_hid_configure: configuring device 56A.44 (WACOM), quirk is 4 (IGNORE)
+</PRE></BLOCKQUOTE>
+
+If you get this message, but with a different quirk, or if the wacom driver
+still is not getting control by this point, there is a possibility that your
+tablet is not listed in any of the driver files. If so, continue on to the
+next section. Otherwise, if you are up and running, skip to
+<A HREF="#viewdata">Viewing the Raw Data</A>.
+<A NAME="newtablet">
+<H3>3.6.5 - Unknown Tablet?</H3>
+To determine whether your device is listed in the driver, we need to determine
+the device identifier. It can be discovered by doing the following:
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa root]# grep -i 56a /var/log/messages | tail -10
+Dec 22 21:03:09 ayukawa /etc/hotplug/usb.agent: Setup mousedev for USB product <B>56a/44/115</B>
+Dec 22 21:27:48 ayukawa kernel: usb.c: USB device 2 (vend/prod <B>0x56a/0x44</B>) is not claimed by any active driver.
+</PRE></BLOCKQUOTE>
+
+<P>In this case, the tablet identifier is 56a/44 (also written 0x56a/0x44).
+The model is determined by the second number; the Intuos2 12x12 is 0x44 for
+instance.
+
+<P>In the wacom.c file, you will find a table called "wacom_ids." Look for
+your device identifier. Only the identifiers listed are handled by the
+wacom driver, so if it is missing, it needs to be added. Also look in
+hid-core.c. It has a table called "hid_blacklist" which identifies devices
+to ignore. The code is a little strange, but PENPARTNER is 0x00, GRAPHIRE is
+0x10, INTUOS is 0x20, PL is 0x30, and INTUOS2 is 0x40. A table entry like
+INTUOS2 + 4 would therefore be 0x44.
+
+<P>If you've gotten this far, and still cannot get it to work, send me email
+with your device identifier and as much of an explanation of where things did
+and did not work as described. I'll see what I can do about at least finding
+out why it did not work. Then we can go on to solutions.
+
+<P>The next section assumes you have things working up to this point.
+
+<A NAME="viewdata">
+<H2>3.7 - Viewing the Raw Data</H2>
+<P>View the raw data from the tablet,
+by returning to the /dev/input directory and streaming the data directly
+from the device. Be patient here because this is where a lot of people
+are getting stuck.
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa usb]# cd /dev/input
+[root@ayukawa input]# xxd event0
+0000000: e65d c33d 597d 0100 0100 4101 0100 0000 .].=Y}....A.....
+0000010: e65d c33d 5c7d 0100 0400 0000 b701 2800 .].=\}........(.
+0000020: e65d c33d d9bb 0100 0100 4101 0000 0000 .].=......A.....
+0000030: e65d c33d dcbb 0100 0400 0000 b701 2800 .].=..........(.
+(Ctrl-C)
+</PRE></BLOCKQUOTE>
+
+First off, you have to move the mouse or tap the pen to get any output.
+Second, you might not get anything at all. Don't panic. This
+seems to happen occasionally. Unfortunately, the work-around is neither
+straightfoward nor foolproof. I am currently talking with Wacom developers
+about this and hopefully we'll get it solved soon.
+
+<P>If absolutely no output occurs, you should check your messages
+file for the following line:
+
+<BLOCKQUOTE><PRE>
+[jej@sasami root]# tail /var/log/messages
+Dec 22 17:31:06 sasami kernel: <B>wacom_intuos_irq: received unknown report #1</B>
+Dec 22 17:31:09 sasami last message repeated 195 times
+</PRE></BLOCKQUOTE>
+
+This is a common failure mode of the driver, and I am investigated the cause.
+Each time this has happened to me though, I <I>have</I> been
+able to get it to work. Simply unload and reload the driver.
+
+<BLOCKQUOTE><PRE>
+[jej@sasami root]# rmmod wacom
+[jej@sasami root]# modprobe wacom
+[jej@sasami root]# tail /var/log/messages
+Dec 22 17:31:31 sasami kernel: usb.c: deregistering driver wacom
+Dec 22 17:31:34 sasami kernel: usb.c: registered new driver wacom
+Dec 22 17:31:35 sasami kernel: input0: Wacom Intuos2 12x12 on usb1:2.0
+Dec 22 17:31:35 sasami kernel: wacom.c: v1.30-j0.2.0 Vojtech Pavlik &lt;vojtech@suse.cz&gt;
+</PRE></BLOCKQUOTE>
+
+<P>Apparently, the device driver and the tablet occassionally get out of
+sync with the tablet thinking it's still in HID mode when in fact it
+should be in "mode 2." By unloading and reloading the driver manually,
+the initialization code has another opportunity to get it right. Try the
+<I>xxd /dev/input/event0</I> again. This time, it <I>should</I> work. If
+not, send me some email. I am trying to create a repeatable test case
+for the Wacom developers, and your problem may be just what we need to
+get this bug fixed.
+
+<P>Incidentally, if you have a program running that is connected to
+/dev/input/event0 (like X or wacdump for instance), it is possible that
+the tablet will not reattach back to the same event. I have seen the
+wacom reattach to /dev/input/event1 when unloading and reloading the
+wacom driver with wacdump running for instance.
+
+<P>You should also try running <i>xxd</i> on /dev/input/mouse0.
+You should get streams of data when the mouse and pen are moved
+around the surface of the tablet. It is this device that X will
+look at for mouse movement. Use Ctrl-C to exit xxd.
+<A NAME="wacdump">
+<H1>4.0 - wacdump</H1>
+<P>wacdump.c parses and displays the raw event output. You must
+be root to run it unless you've set the permissions on /dev/input/event0
+such that you can read it. This program can run simultaneously with X,
+but it's best if X has not be configured for the tablet yet.
+wacdump is enabled by default in the configure script, so it should be
+built automatically when you run <I>make</I>.
+
+<P>The command line usage of wacdump (wacpack-0.2.0) is pretty simple:
+
+<BLOCKQUOTE><PRE>
+Usage: wacdump [-d device]
+ -?, -h, --help - usage
+ -d, --device device - use specified device
+</PRE></BLOCKQUOTE>
+
+<P>If you know that the tablet is attached to a device other than
+/dev/input/event0, you can override it with the -d option, but otherwise,
+the default is correct. If you connect the /dev/input/event0 and get an
+end-of-file error, then the wacom is probably attached to a different event.
+
+<P>When you run wacdump, nothing will happen until you place a mouse or
+pen near the surface. After that, the screen will clear and you will see
+a screen similar to the following output:
+
+<BLOCKQUOTE><PRE>
+Wacom Intuos2 12x12 bus=3, vndr=56A, prd=44, ver=115
+
+ KEY=00000000.00000000 0000 0000 00000000
+ ABS=00000000.00000000 0000 0000 00000000
+ MSC=3DD488B5.0000F385 0004 0000 0028102E
+
+ X=+17623 (+00000 .. +30480) Y=+10730 (+00000 .. +31680)
+ RZ=-00058 (-00900 .. +00899) THROTTLE=+00000 (-01023 .. +01023)
+ WHEEL=+00000 (+00000 .. +01023) PRESSURE=+00005 (+00000 .. +01023)
+DISTANCE=+00007 (+00000 .. +00015) TILT_X=+00094 (+00000 .. +00127)
+ TILT_Y=+00122 (+00000 .. +00127)
+
+ WHEEL=+00000
+ SERIAL=0028102E
+
+ LEFT= RIGHT= MIDDLE= SIDE=
+ EXTRA= PEN= RUBBER= BRUSH=
+ PENCIL= AIR= MOUSE= LENS=
+ TOUCH= STYLUS= STYLUS2=
+</PRE></BLOCKQUOTE>
+
+<P>This output comes from version 0.3.2 of the wacdump program.
+
+<P>The first row displays the model number, vendor id, product id, and
+revision number.
+
+<P>The next block shows events as they arrive. Only the
+event types that the device claims to generate are displayed.
+The quickly changing numbers are timestamps.
+
+<P>The middle block shows absolute positions of various device parameters.
+It also shows the ranges that the device claims to provide. If your device
+exceeds these ranges, please let me know so I can fix the
+driver. All the available positions are displayed, even though some of
+them are not available for certain pointers. Different tablets will have
+different capabilities; these are capabilities registered specifically for
+your tablet by the wacom.o kernel driver.
+
+<P>The second WHEEL value is from the relative events channel. Presently,
+only the beta drivers report relative events.
+
+<P>The serial number of the current stylus is displayed next. When two
+pointers are present, the serial number will alternate. This is especially
+true of Intuos2 models and possibly less accurate of other models.
+
+<P>Lastly, the button positions are displayed. Some mice have more buttons
+than others; the ones displayed are the ones that the tablet offers. Buttons
+like "Pen" or "Airbrush" are displayed as "Down" when the tool is in proximity
+to the surface.
+
+<P>Whenever you are curious if the device is working properly, wacdump is
+a useful tool for determine exactly what is coming from the tablet. You can
+run it in conjunction with X or without. The display is constrained to 80x25
+so it can also be used on the main console terminal.
+<A NAME="x11">
+<H1>5.0 - Configuring X11</H1>
+Two steps must be completed to get X to recognize the Wacom. First,
+you need to add some lines to XF86Config to inform X of the tablet's
+existence. Second, you need to update the XInput driver that pertains
+to the Wacom since the one that ships with XFree86 is not very functional.
+Neither driver holds a candle to the windows driver though, so you'll
+have to take what you get for the time being. Updates to the XFree86 driver
+are available in the stable and beta releases on the
+<A HREF="#download">Downloading the Code</A> page. You may
+also check <A HREF="http://people.mandrakesoft.com/~flepied/projects/wacom/">Lepied's Wacom Site</A> which
+has source code up to version 26 as of this writing.
+<A NAME="inputdev">
+<H2>5.1 - Adding the InputDevices</H2>
+<P>The X Window system identifies the stylus and eraser on your tablet as
+XInput devices. Applications that want to know the absolute position of
+your stylus can request that information directly, and this generally
+bypasses whatever the mouse happens to be doing at the time. This design
+is not without problems though. For one, changing your setup or adding
+a new tool requires making changes to the XF86Config file and restarting
+X. This should be fixed in the future.
+
+<P>For now however, add the InputDevice sections to your XF86Config file. This
+assumes you are running XFree86 4.x. <SPAN CLASS=diff>On some distributions,
+this file is called XF86Config-4</SPAN>.
+
+<BLOCKQUOTE><PRE>
+Section "InputDevice"
+ Driver "wacom"
+ Identifier "cursor"
+ Option "Device" "/dev/input/event0"
+ Option "Type" "cursor"
+ Option "Mode" "relative"
+ Option "USB" "on"
+ Option "Speed" "3.0"
+ <B>Option "Threshold" "10"</B>
+EndSection
+
+Section "InputDevice"
+ Driver "wacom"
+ Identifier "stylus"
+ Option "Device" "/dev/input/event0"
+ Option "Type" "stylus"
+ Option "Mode" "absolute"
+ Option "USB" "on"
+ Option "Tilt" "on"
+ #Option "TiltInvert" "on"
+ <B>Option "Threshold" "10"</B>
+EndSection
+
+Section "InputDevice"
+ Driver "wacom"
+ Identifier "eraser"
+ Option "Device" "/dev/input/event0"
+ Option "Type" "eraser"
+ Option "Mode" "absolute"
+ Option "USB" "on"
+ Option "Tilt" "on"
+ #Option "TiltInvert" "on"
+ <B>Option "Threshold" "10"</B>
+EndSection
+
+#Section "InputDevice"
+# Driver "wacom"
+# Identifier "tablet"
+# Option "Device" "/dev/input/event0"
+# Option "Type" "beta"
+#EndSection
+</PRE></BLOCKQUOTE>
+
+<P>The first three sections identify the cursor, stylus, and eraser devices
+to XInput. Notice that all three reference /dev/input/event0. Additionally,
+the TiltInvert option has been commented out. You may want to experiment
+with these values.
+
+<P>The fourth InputDevice section is for the new beta driver. Unless you
+are actually testing this code, I do not recommend using this section, at
+least not at present. There is no harm in leaving it in the file, commented
+in or out, just so long as it is not used in the ServerLayout section
+described later.
+
+<P><B>The threshold value is very important.</B> Various wacom tablets behave
+differently in terms of how the "TOUCH" event is detected. On some tablets,
+the device itself makes the determination, and on others, the driver detects
+the event by examining the pressure value. To make matters more complex,
+the ranges are all different so what is good for my Intous2 may not be good
+for your Graphire. I am providing a default value of 10. If the pressure
+exceeds that value, a "TOUCH" event is sent to the XInput system. If this
+is too hard, you may want to reduce it. If the value is too low, you will
+encounter the reviled "spurious touch" problem where the stylus clicks
+randomly as you move it around the screen.
+
+<P>Also, people using serial tablets will need completely different settings.
+Please see <A HREF="http://people.mandrakesoft.com/~flepied/projects/wacom/">Lepied's Wacom Site</A> for
+more details.
+
+<P>I am including a copy of the man page for the driver. Some of the fields
+pertain only to the serial device, and some of the fields may be deprecated
+under XFree86 4.2.0. Try them out, see what happens.</P>
+
+<BLOCKQUOTE><PRE>
+WACOM(4x) XFree86 WACOM(4x)
+
+NAME
+ wacom - Wacom input driver
+
+SYNOPSIS
+ Section "InputDevice"
+ Identifier "idevname"
+ Driver "wacom"
+ Option "Device" "devpath"
+ ...
+ EndSection
+
+DESCRIPTION
+ wacom is an XFree86 input driver for Wacom devices.
+
+ The wacom driver functions as a pointer input device, and may be used
+ as the X server's core pointer.
+
+SUPPORTED HARDWARE
+ This driver supports the Wacom IV and Wacom V protocols. Preliminary
+ support is available for USB devices on some Linux platforms.
+
+CONFIGURATION DETAILS
+ Please refer to XF86Config(5x) for general configuration details and
+ for options that can be used with all input drivers. This section only
+ covers configuration details specific to this driver.
+
+ Multiple instances of the Wacom devices can cohabit. It can be useful
+ to define multiple devices with different active zones. Each device
+ supports the following entries:
+
+ Option "Type" "stylus"|"eraser"|"cursor"
+ sets the type of tool the device represent. This option is
+ mandatory.
+
+ Option "Device" "path"
+ sets the path to the special file which represents serial
+ line where the tablet is plugged. You have to specify it
+ for each subsection with the same value if you want to have
+ multiple devices with the same tablet. This option is
+ mandatory.
+
+ Option "USB" "on"
+ tells the driver to dialog with the tablet the USB way.
+ This option is only available on some Linux platforms.
+
+ Option "DeviceName" "name"
+ sets the name of the X device.
+
+ Option "Suppress" "Inumber"
+ sets the position increment under which not to transmit
+ coordinates. This entry must be specified only in the
+ first Wacom subsection if you have multiple devices for one
+ tablet. If you don’t specify this entry, the default value
+ is computed to
+
+ Option "Mode" "Relative"|"Absolute"
+ sets the mode of the device.
+
+ Option "Tilt" "on"
+ enables tilt report if your tablet supports it (ROM version
+ 1.4 and above). If this is enabled, multiple devices at
+ the same time will not be reported.
+
+ Option "HistorySize" "number"
+ sets the motion history size. By default the value is zero.
+
+ Option "AlwaysCore" "on"
+ enables the sharing of the core pointer. When this feature
+ is enabled, the device will take control of the core
+ pointer (and thus will emit core events) and at the same
+ time will be able, when asked so, to report extended
+ events. You can use the last available integer feedback to
+ control this feature. When the value of the feedback is
+ zero, the feature is disabled. The feature is enabled for
+ any other value.
+
+ Option "TopX" "number"
+ X coordinate of the top corner of the active zone.
+
+ Option "TopY" "number"
+ Y coordinate of the top corner of the active zone.
+
+ Option "BottomX" "Inumber"
+ X coordinate of the bottom corner of the active zone.
+
+ Option "BottomY" "number"
+ Y coordinate of the bottom corner of the active zone.
+
+ Option "KeepShape" "on"
+ When this option is enabled, the active zone begins
+ according to TopX and TopY. The bottom corner is adjusted
+ to keep the ratio width/height of the active zone the same
+ as the screen while maximizing the area described by TopX,
+ TopY, BottomX, BottomY.
+
+
+ Option "DebugLevel" number
+ sets the level of debugging info reported.
+
+ Option "BaudRate" "38400", "19200" or "9600" (default)
+ changes the serial link speed. This option is only availâ€
+ able for wacom V models (Intuos).
+
+ Option "Serial" "number"
+ sets the serial number associated with the physical device.
+ This allows to have multiple devices of the same type (i.e.
+ multiple pens). This option is only available on wacom V
+ devices (Intuos). To see which serial number belongs to a
+ device, you have to set the DebugLevel to 6 and watch the
+ output of the X server.
+
+ Option "Threshold" "number"
+ sets the pressure threshold used to generate a button 1
+ events of stylus devices for some models of tablets (Intuos
+ and Graphire).
+
+SEE ALSO
+ XFree86(1), XF86Config(5x), xf86config(1), Xserver(1), X(7x).
+
+AUTHORS
+ Frederic Lepied &lt;lepied@xfree86.org&gt;
+
+4.2.0 Version WACOM(4x)
+</PRE></BLOCKQUOTE>
+<A NAME="mouse1">
+<H2>5.2 - Mouse1</H2>
+<P>Adding the Mouse1 device is <I>probably not something you want to do</I>,
+and Redhat's Anaconda program will do it for you if you boot the machine with
+the tablet plugged in. You'll need to be careful about this.
+
+<P>When you use the mouse1 input device, the data flows from the wacom
+kernel driver, through the event subsystem, down into the mousedev driver,
+out the /dev/input/mouse0 device, and finally into the XInput mouse driver.
+You effectively lose all your absolute positioning information because the
+mousedev driver converts it into relative data. Additionally, the XFree86
+wacom driver does not get control of the cursor because mouse1 is providing
+those events.
+
+<P>Therefore, if you have a Mouse1 section, leave it. Redhat 8.0 at least,
+expects it to be there; however, you will not be using it, so make certain
+that it is commented out of the section covered next.
+<A NAME="srvlayout">
+<H2>5.3 - ServerLayout</H2>
+<P>The ServerLayout section describes what devices the X server will use.
+Modify the ServerLayout section to reflect the new devices. Make certain
+to comment out the Mouse1 device.
+
+<BLOCKQUOTE><PRE>
+Section "ServerLayout"
+ Screen 0 "Screen0" 0 0
+ InputDevice "Mouse0" "CorePointer"
+ InputDevice "Keyboard0" "CoreKeyboard"
+ <B>InputDevice "cursor" "SendCoreEvents"
+ InputDevice "stylus" "SendCoreEvents"
+ InputDevice "eraser" "SendCoreEvents"</B>
+ #InputDevice "Mouse1" "SendCoreEvents"
+ #InputDevice "tablet" "SendCoreEvents"
+EndSection
+</PRE></BLOCKQUOTE>
+
+<P>This section determines which devices are actually used by the server. In
+the case above, the cursor, stylus, and eraser devices are selected while
+the mouse1 and tablet devices are commented out. At present, this is the
+correct configuration for proper operation of the tablet whether you are
+using the beta package or the production package.
+
+<P>The new wacom_drv.o driver contains two independent implementations of the
+tablet code. Using the "tablet" device instead of the cursor/stylus/eraser
+devices will enable that code. Presently, that will do little for you since
+the code is fairly volatile and is simply a testbed for various design ideas.
+As it becomes more useful, I will update this section.
+
+<P>You have completed the XF86Config file changes. By aware that if you reboot
+your computer with the Wacom plugged in and Redhat's Anaconda program notices,
+it will treat the tablet as a USB mouse and reconfigure this file incorrectly.
+You may need to go back and check the file to ensure that everything is
+still correct afterwards. Rebooting with the device detached seems to
+reverse the process, but again, you should check the file to be certain.
+My recommendation is to tell Anaconda to ignore the tablet until the device
+detection works properly.
+<A NAME="wacomdrv">
+<H2>5.4 - Updating wacom_drv.o</H2>
+<P>There are a number of wacom_drv.o files about. XFree86 is shipping
+version 23. Redhat 8.0 is shipping version 25. Lepied's page has the
+source code for 26, but the binary identifies itself as 23ss1. The
+<A HREF="#download">Downloading the Code</A> page currently
+offers source and binary for 26-j0.2.0 in the stable package
+and 26-j0.3.2a in the beta version. In total, I recommend
+using the drivers in the packages that I provide since they correspond with
+what is shown in this document. Any new functionality that gets added will
+be built on top of these files as well.
+
+<P>Note: the binary wacom_drv.o file is located in the prebuilt directory
+of the stable and beta packages. Building this file from source (which is
+provided) is a major hassle, and I do not recommend it unless you are
+developer yourself or the binary is simply not compatible with your system.
+Instructions for rebuilding from source are available in the Appendix
+under <A HREF="#builddrv">Building wacom_drv.o From Scratch</A>.
+
+<P>With that said, locate and replace wacom_drv.o. Using <i>locate</i> again, we find
+the driver in the X11 tree. This directory is pretty standard, so it is
+unlikely that your file will be located elsewhere. Note the assumption is
+that you are using XFree86 4.x. Anything from the 3.x version probably won't
+work.
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa root]# locate wacom_drv.o
+/usr/X11R6/lib/modules/input/<B>wacom_drv.o</B>
+[root@ayukawa root]# cd /usr/X11R6/lib/modules/input
+[root@ayukawa input]# cp <B>wacom_drv.o</B> /home/jej/src/wacom/<B>wacom_drv_old.o</B>
+[root@ayukawa input]# cp /home/jej/src/wacom/prebuilt/<B>wacom_drv.o wacom_drv.o</B>
+</PRE></BLOCKQUOTE>
+
+Again, the old file is copied away, and replaced with the newer version. The
+binary modules are presently built on a Redhat 8.0 system using gcc 3.2 under
+glibc. This should work for most, if not all, major distributions. If it
+does not load on yours, please let me know.
+
+<A NAME="restartx">
+<H2>5.5 - Restart X</H2>
+<P>Finally, restart X. You may wish to do this from runlevel 3 for
+testing purposes.
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa root]# init 3
+...processes starting and stopping...
+[root@ayukawa root]# startx
+</PRE></BLOCKQUOTE>
+
+If the X server dies, you can always back-out the changes to the XF86Config
+file and try again. Worse case, copy the wacom_drv_old.o file back to
+its original place. But first, look at the XFree86 log file for clues.
+You might want to do this even if everything works correctly. When things
+are running right, the following lines appear in my log file.
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa root]# grep -i wacom /var/log/XFree86.0.log
+(II) LoadModule: "wacom"
+(II) Loading /usr/X11R6/lib/modules/input/wacom_drv.o
+(II) Module wacom: vendor="The XFree86 Project"
+(II) Wacom driver level: <B>26-j0.2.0</B> $
+(II) XINPUT: Adding extended input device "eraser" (type: Wacom Eraser)
+(II) XINPUT: Adding extended input device "stylus" (type: Wacom Stylus)
+(II) XINPUT: Adding extended input device "cursor" (type: Wacom Cursor)
+(==) Wacom Kernel Input device name: "Wacom Intuos2 12x12"
+(==) Wacom tablet maximum X=30480 maximum Y=30480 X resolution=0 Y resolution=0
+suppress=0
+(==) Wacom Cursor top X=0 top Y=0 bottom X=30480 bottom Y=30480
+(==) Wacom Stylus top X=0 top Y=0 bottom X=30480 bottom Y=30480
+(==) Wacom Eraser top X=0 top Y=0 bottom X=30480 bottom Y=30480
+</PRE></BLOCKQUOTE>
+
+Notice the driver version 26-j0.2.0 above. This is the new stable
+wacom_drv.o driver. The beta driver is currently 26-j0.3.2a.
+
+<P>First things first, you should lift the mouse off the tablet and place it
+back down. This seems to help reset things internally. When you replace the
+mouse, the cursor should jump to that portion of the screen. If everything is
+working correctly, the mouse should work in absolute mode with the four
+corners of the tablet corresponding with the four corners of the screen. If
+the cursor stops short of an edge, then the kernel driver limits are probably
+incorrect. Please let me know so that I can update the driver.
+
+<P>Next, remove the mouse and try using the eraser end of the stylus.
+Hovering over the surface of the tablet should move the cursor. Touching the
+eraser tip to the surface should generate a click. If you invert the
+pen and use the stylus tip, you should get a similar result. If the pen
+tip generates spurious touch events, you will need to increase the threshold
+value in the InputDevice section. Wacdump is useful for determining the
+appropriate value. My pen pressure oscillates between 0 and 6 so I use
+a threshold of 10 to compensate.
+<A NAME="ptrstat">
+<H2>5.6 - Check the Pointer Status</H2>
+<P>You can check the XInput pointer status by using <i>xsetpointer</i>
+as below. The man page states that calling xsetpointer with the name of
+a particular device will set it as the primary pointing device. So far,
+that has not been the case in my experience; however, that is the subject
+of current development, so try it, and if it doesn't work, stay tuned.
+
+<BLOCKQUOTE><PRE>
+[root@ayukawa log]# xsetpointer -l
+"eraser" [XExtensionDevice]
+"stylus" [XExtensionDevice]
+"cursor" [XExtensionDevice]
+"Mouse0" [XPointer]
+"keyboard" [XKeyboard]
+</PRE></BLOCKQUOTE>
+
+<A NAME="gimp">
+<H1>6.0 - Working With Gimp</H1>
+<DIV CLASS=diff>It has been suggested that gimp should be recompiled from
+source (v1.2.3) on Mandrake 9.0. This does not seem to be true for Redhat
+8.0.
+</DIV>
+
+<P>Bring up <i>gimp</i> and select "File|Dialogs|Input Devices".
+You will see a drop-down list with all three devices present. After enabling
+them, you can see their respective statuses by calling up "File|Dialogs|Device
+Status". It has been recommended that the devices be set to "Screen" mode
+rather than "Window."
+
+<P>I have successfully been able to use gimp with several different pens,
+including the tips and erasers. Tilt does not appear to be used by gimp
+at present, but pressure works fine.
+<A NAME="changelog">
+<H1>7.0 - Changelog</H1>
+<UL>
+<LI>2002-12-22 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Added serial code to wacdump<BR>
+* Added serial protocols IV 1.0 - 1.4.<BR>
+* Added serial protocol V 1.0 - 1.1.<BR>
+* Intuos mouse wheels are now positive when rolled backwards per PS/2
+ Intellimouse convention<BR>
+* Added Intuos2 to xf86Wacom.c<BR>
+* Added build help for Debian Stable users<BR>
+</BLOCKQUOTE>
+
+<LI>2002-12-17 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Merged code from 2.4.20 hid-core.c<BR>
+* Added configuration code to handle kernels still at 2.4.19<BR>
+* Split wacdump curses code into wacscrn.c to solve namespace collision<BR>
+* Added volito to wacom.c<BR>
+</BLOCKQUOTE>
+
+<LI>2002-12-16 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* House cleaning on the website.<BR>
+* Dropped the Intuos constraint from the document name.<BR>
+* Added the expectations page.<BR>
+* Changed the internal indexing to identifier rather than chapter.
+</BLOCKQUOTE>
+<LI>2002-12-15 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Removed some debugging code, redeployed package with wacom_drv.o version
+ 0.3.2a.<BR>
+* New build has fix for 4D mouse wheel.<BR>
+* wcm-beta.c fleshed out with new driver code.<BR>
+</BLOCKQUOTE>
+<LI>2002-12-13 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Added development page.<BR>
+* New build has relative events and fix for 2D mouse wheel.<BR>
+* wcm-beta.c added to build.<BR>
+</BLOCKQUOTE>
+<LI>2002-12-09 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Added detail on "Threshold" parameter.<BR>
+* Included man page.
+</BLOCKQUOTE>
+<LI>2002-11-24 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Updated docs for version 0.2.0.<BR>
+* Package uses configure-based scripts.<BR>
+</BLOCKQUOTE>
+<LI>2002-11-14 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Moved wacom_drv.o to prebuilt directory.<BR>
+* Added xf86Wacom.c (version 26) to package along with changes to Makefile<BR>
+* Extended documentation on X11 setup including caveat over Mouse1 device.<BR>
+* Added xf86Wacom.c build instructions.<BR>
+* Updated wacdump (0.2) to display identifiers, maximum values, and all
+ registered event types.<BR>
+* Added highlighting in wacdump to display button down state.<BR>
+</BLOCKQUOTE>
+
+<LI>2002-11-11 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* wacom.c - fixed ranges for intuos2 6x8<BR>
+</BLOCKQUOTE>
+
+<LI>2002-11-10 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* wacdump.c - updated display to identify values<BR>
+* wacom.c - fixed ranges for intuos2 12x12<BR>
+</BLOCKQUOTE>
+
+<LI>2002-11-09 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* wacom.c - merged some of Cheng's changes, fixes to the
+pressure calculation, device ranges.<BR>
+* wacom.c - added an extra device (0x47) for M. T.<BR>
+* wacdump.c - added text messages for event types<BR>
+* wacdump.c - added usage and device override option<BR>
+* wacdump.c - exits when channel is closed.<BR>
+* Fixed module dependency problem in wacom.o and hid.o<BR>
+* Added clarification for devfs and Mandrake 9.0<BR>
+</BLOCKQUOTE>
+
+<LI>2002-11-06 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Busted page out into discrete chapters.<BR>
+* Added some php magic to turn it into an active book.<BR>
+* Added Mandrake 9.0<BR>
+* Gimp may need to be compiled from source (1.2.3)
+</BLOCKQUOTE>
+<LI>2002-11-05 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+<BLOCKQUOTE>
+* Updated wacom driver with Christer Nilsson's 2.4.20 patch<BR>
+* Added RedHat 7.3 success<BR>
+* Added more discussion on the device driver election.<BR>
+* Added more discussion on HID mode 1 and the correct mode 2.<BR>
+* Added usbmouse.c and hid-core.c to wacpack-0.1b.<BR>
+</BLOCKQUOTE>
+<LI>2002-11-04 - John E. Joganic &lt;jej@j-arkadia.com&gt;<BR>
+
+<BLOCKQUOTE>
+* Added update section.<BR>
+* Added success stories.<BR>
+* Added RedHat 7.2 specific information<BR>
+* Added more information about "unknown report #1"<BR>
+</BLOCKQUOTE>
+</UL>
+<A NAME="contact">
+<H1>8.0 - Contacts</H1>
+<P>John Joganic can be contacted at the following email address:
+<A HREF="mailto: jej@j-arkadia.com?SUBJECT=WACOM">jej@j-arkadia.com</A>.
+If at all possible, please include the word "WACOM" in the subject line.
+I receive hundreds of messages a day, and this little addition will help
+me find your message in the tons of spam.
+<A NAME="appendix">
+<H1>9.0 - Appendix</H1>
+<P>This section is for everything that is either not critical or simply
+too complex to describe in the document above without becoming overly
+distracting.
+<A NAME="builddrv">
+<H2>9.1 - Building wacom_drv.o From Scratch</H2>
+<H3>Introduction</H3>
+
+<P>I should tell you out-right that this is an ugly, time consuming process.
+If you manage to get this working in fewer steps, by all means, let me know.
+
+<P>Why would you want to do this? Two reasons. One, you are a developer
+and need to make changes to the source code directly. Two, your distribution
+uses c libraries or configuration options that are not compatible with the
+wacom_drv.o file that I provide. People running libc5 for instance, would
+need to build their own driver.
+
+<BLOCKQUOTE><DIV CLASS=diff>
+Timothy Klein has submitted a brief howto for compiling on Debian Stable
+which is still running XFree86 4.1 as of this writing. It covers steps
+one through four of this document, and a savvy developer should be able
+to figure out step five on his own. If someone solves step five and
+generates a patch to Makefile.am, I'll see what I can do about getting
+it into the configuration script. That document is on the <A HREF="#debwcmdrv">Building wacom_drv.o On Debian Stable</A> page.
+</DIV></BLOCKQUOTE>
+
+<P>You will need the X source code to rebuild the wacom_drv.o driver.
+The build configuration for X generates a number of header files that are
+necessary but not installed by default on most distributions. Consequently,
+you will need to not only get the source, but build it, practically in its
+entirety. Then, after all that, the configure script can be instructed to
+hook into the X build tree and rebuild xf86Wacom.c at any time without
+having to rebuild X again.
+
+<P>Since I am running Redhat 8.0 and cannot really pull down the original
+XFree86 4.2.0 source code, compile it, and expect it to work on my
+system, I need to instead use the source RPM provided by Redhat. If you
+choose to go this route, I provide pretty detailed instructions for making
+this work. If your distribution works differently, or you are using Gentoo
+where most everything is source code by default, you'll need to handle this
+as best as possible according to your particular situation.
+
+<P><H3>Step One: Get The Source</H3>
+
+<P>On Redhat 8.0, I discovered the version number for my currently installed
+XFree86 packages by running <I>rpm -q XFree86</I>. This reported version
+4.2.0-72, therefore the source package is XFree86-4.2.0-72.src.rpm.
+I downloaded the package from Redhat directly and installed it to the system
+as follows:
+
+<BLOCKQUOTE><PRE>
+[root@sen src]# rpm -ivh XFree86-4.2.0-72.src.rpm
+ 1:XFree86 ########################################### [100%]
+</PRE></BLOCKQUOTE>
+
+<P>This installs a number of files to the /usr/src/redhat directory,
+particularly in the SOURCES and SPECS subdirectories. Other distributions
+undoubtedly install elsewhere. Look for the XFree86.spec file which should
+be located in the SPECS directory. This file contains all the information
+necessary to patch the orginal XFree86-4.2.0 source code to the level that
+Redhat is distributing in their regular binary package. The source code
+and patch files are located in SOURCES.
+
+<H3>Step Two: Build the Source</H3>
+
+<P>This step describes how to build the source from the RPM itself. If
+you are building from some other mechanism, I honestly cannot offer much
+assistance since I generally don't build my X system from scratch.
+If you'd like to write up a short section on building the
+server for your particular distribution, I would be happy to include it here.
+
+<P>Next, you don't actually have to build the entire thing. The
+point at which the xf86Wacom.c driver can be built however,
+is not until somewhere in the middle of the build process. The driver
+depends on a number of header files that are created dynamically so until
+they are generated, wacom_drv.o cannot be compiled. My solution
+was to open a separate terminal in the wacom driver directory and
+periodically attempt to build it. When it successfully built, I stopped
+the X build process. Here's how to build the source for an RPM that's
+been exploded out into the SPECS and SOURCES directories.
+
+<BLOCKQUOTE><PRE>
+[root@sen root]# cd /usr/src/redhat
+[root@sen redhat]# rpmbuild -bc SPECS/XFree86.spec
+</PRE></BLOCKQUOTE>
+
+<P>Not every distribution has <I>rpmbuild</I>; try using just <i>rpm</i>
+instead. At some point, Redhat split the build functionality into separate
+programs. If after looking through the <i>rpm</i> man page, you still
+cannot get this to work, send me some email, and I'll look into it.
+
+<P>The important item is the "-bc" option of <I>rpmbuild</I> which unpacks,
+patches, and builds the source without actually installing. While it is also
+possible to simply unpack and patch using the "-bp" option, there does not
+seem to be a way to just build. The "-bc" option simply deletes all the
+files provided by "-bp" and recreates them again. The downside of this is
+that if you wanted to simply unpack, patch, and then copy the new xf86Wacom.c
+file over the old one, you'll find that the build step deletes it and starts
+over again. I have gotten this to work by creating a new patch file, but
+this requires a bit more effort, so I don't recommend it right off.
+
+<H3>Step Three: Build the Original Driver</H3>
+
+<P>The xf86Wacom.c file is buried pretty deep in the X build tree. If it
+is in a different location than the one I have provided below, try using
+<I>find . -name xf86Wacom.c</I> from the BUILD directory.
+
+<BLOCKQUOTE><PRE>
+[root@sen redhat]# cd BUILD/XFree86-4.2.0/xc/programs/Xserver/hw/xfree86/input/wacom
+[root@sen wacom]# ls
+Imakefile wacom.man xf86Wacom.c.Wacom-USB-driver-a25-update
+Makefile xf86Wacom.c
+</PRE></BLOCKQUOTE>
+
+The "a25-update" file is the original xf86Wacom.c file before Redhat's patch.
+If you open xf86Wacom.c, you'll find that it is version 25, at least as
+of this writing and this distribution. The presence of the Makefile means
+that the configuration has at least been run for this directory. If you
+have built a sufficient portion of the X source files, then all the header
+files that you need have been generated, and you can build xf86Wacom.c. Try
+it, and if it does not build, wait a bit. The absence of xf86Version.h
+for instance, is a good indication that the build process is not ready.
+
+<BLOCKQUOTE><PRE>
+[root@sen wacom]# make
+rm -f xf86Wacom.o
+gcc -O2 -march=i386 ... -c xf86Wacom.c
+rm -f wacom_drv.o
+ld -r xf86Wacom.o -o wacom_drv.o
+</PRE></BLOCKQUOTE>
+
+<H3>Step Four: Build the New Driver</H3>
+
+<P>With a valid build environment, it should be possible to copy the
+updated driver source over the old one, run <i>make</i>, and get an updated
+driver.
+
+<BLOCKQUOTE><DIV class=diff>The beta package contains a version xf86Wacom.c that depends
+on wcm-beta.c and wcm-beta.h. If you are compiling this version, you will
+need to copy all three files. Secondly, the driver will build and link, but
+will not be usable since the current X build environment does not expect the
+wcm-beta code to be compiled. This problem is addressed correctly in the
+automated build process discussed below.
+</DIV></BLOCKQUOTE>
+
+<BLOCKQUOTE><PRE>
+[root@sen wacom]# cp xf86Wacom.c xf86Wacom_old.c
+[root@sen wacom]# cp /home/jej/src/wacom/src/xf86Wacom.c xf86Wacom.c
+[root@sen wacom]# make
+rm -f xf86Wacom.o
+gcc -O2 -march=i386 ... -c xf86Wacom.c
+rm -f wacom_drv.o
+ld -r xf86Wacom.o -o wacom_drv.o
+</PRE></BLOCKQUOTE>
+
+If the source did not build correctly the second time, it is possible
+that the Imake-based build environment has generated a makefile that
+will not work correctly for the new code. Your options are to generate
+a patch file that can be applied by the build process, hack the makefile
+in the driver directory to work properly, or to find some
+way to get Imake to reconfigure. This is probably less of a hassle for people
+building from source, but to be fair, all the commands and information you
+need to reconfigure from RPM are listed in the beginning of the build output.
+You might also want to continue ahead to see if the default build rule does
+not work for you.
+
+<H3> Step Five: Automating the Build Process</H3>
+
+<P>Copying the updated source file into the build tree and compiling from
+there is not convenient. By configuring the package with the --with-xf86
+option set to the XFree86 build tree and enabling the driver using the
+--enable-wacomdrv option, you can build the driver outside of the X build
+tree.
+<BLOCKQUOTE><PRE>
+[jej@ayukawa wacom]$ ./configure \
+ --with-xf86=/usr/src/redhat/BUILD/XFree86-4.2.0 \
+ --enable-wacomdrv
+...
+BUILD ENVIRONMENT:
+ XFree86 - yes
+ BUILD OPTIONS:
+ wacom_drv.o - yes
+[jej@ayukawa wacom]$ make
+</PRE></BLOCKQUOTE>
+
+<P>The makefile rule which builds the driver is contained within src/Makefile.am
+and is modified according to the configuration to generate a rule similar to
+this in src/Makefile:
+
+<BLOCKQUOTE><PRE>
+xf86Wacom.o: xf86Wacom.c
+ gcc -O2 -march=i386 -mcpu=$(ARCHITECTURE) -pipe -ansi \
+ -pedantic -Wall -Wpointer-arith -fno-merge-constants \
+ -I. -I$(XF86_DIR)/programs/Xserver/hw/xfree86/common \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/loader \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/os-support \
+ -I$(XF86_DIR)/programs/Xserver/include \
+ -I$(XF86_DIR)/programs/Xserver/mi \
+ -I$(XF86_DIR)/exports/include/X11 \
+ -I$(XF86_DIR)/include/extensions \
+ -I$(XF86_DIR) \
+ -I$(XF86_DIR)/exports/include \
+ -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE \
+ -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE \
+ -DSHAPE -DXINPUT -DXKB -DLBX -DXAPPGROUP -DXCSECURITY \
+ -DTOGCUP -DXF86BIGFONT -DDPMSExtension -DPIXPRIV -DPANORAMIX \
+ -DRENDER -DGCCUSESGAS -DAVOID_GLYPHBLT -DPIXPRIV \
+ -DSINGLEDEPTH -DXFreeXDGA -DXvExtension -DXFree86LOADER \
+ -DXFree86Server -DXF86VIDMODE -DXvMCExtension \
+ -DSMART_SCHEDULE -DBUILDDEBUG -DXResExtension \
+ -DX_BYTE_ORDER=X_LITTLE_ENDIAN -DNDEBUG -DFUNCPROTO=15 \
+ -DNARROWPROTO -DIN_MODULE -DXFree86Module -DLINUX_INPUT \
+ -o xf86Wacom.o -c xf86Wacom.c
+</PRE></BLOCKQUOTE>
+
+<BLOCKQUOTE><DIV class=diff>In the beta package, a similar rule applies to wcm-beta.c.
+</DIV></BLOCKQUOTE>
+
+<P>The options and directories specified come directly from the output of the
+make command in the previous step. All the root and parent directories have
+been replaced with the macro XF86_DIR which in this case is set by the
+configuration script to /usr/src/redhat/BUILD/XFree86-4.2.0/xc. If the
+options that you see in your build are identical to those above, then the
+default rule will work for you now. If not, you'll need to make some
+alterations. You can update the Makefile.am file and rerun automake,
+update the Makefile.in and rerun configure, or just update the Makefile
+directly.
+
+<P>So long as the X build tree exists, the include directories will point to
+the correct locations, and the driver will build. If space is an issue, you
+can probably remove all the non-essential directories, but be careful; the
+dependency tree in X is huge.
+
+<H3>Conclusion</H3>
+
+<P>Although a bit of a hassle, it is possible to compile the xf86Wacom.c
+file into the wacom_drv.o driver from outside the X build tree. If you
+have any comments, questions, or suggestions regarding this process,
+please send email to <A HREF="mailto:jej@j-arkadia.com?SUBJECT=WACOM_DRV">jej@j-arkadia.com</A> with a subject of "WACOM_DRV".
+<A NAME="debwcmdrv">
+<H2>9.2 - Building wacom_drv.o On Debian Stable</H2>
+The following documentation for building wacom_drv.o on Debian Stable
+was written by Timothy Klein. If you have any problems or questions,
+go ahead and post them to the list, or send email to me directly. If
+I cannot provide a satisfactory answer, I'll forward your email on to
+Timothy.
+
+<BR><BR><BR><BR>
+
+<PRE>
+Just thought I would give you some brief notes on compiling wacom_drv.o
+on Debian Stable. This must be done, as the Stable version of Debian
+has XFree86 v. 4.1.0.1, and your pre-compiled module is for a later
+version of X (4.2?). X complains about the server having ABI 2, and the
+module having ABI 3, and refuses to load. So one must compile the
+wacom_drv.o module for Debian Stable.
+
+The good news is that, while it requires a *huge* download, and takes a
+bit of time, it is easy.
+
+-------------------------------------------------------------
+Debian wacom_drv.o Compilation
+
+<B>1. Download the Debian source package for XFree86.</B>
+
+For Debian Stable this is three files:
+
+xfree86_4.1.0-16.diff.gz
+xfree86_4.1.0-16.dsc
+xfree86_4.1.0.orig.tar.gz
+
+Note that these are *big* files. The 'diff' file is 1.5 megabytes, and
+the 'orig' file is 52 megabytes. So if you have a 28.8 modem
+connection, this is going to be painful.
+
+These files can be obtained automatically by using the command:
+
+apt-get source xfree86
+
+It is not necessary to be root to download the source package. After it
+is downloaded, the package will be automatically extracted. Using this
+method requires that you have a valid 'source' line in your
+'/etc/apt/sources.list' file, though.
+
+You can also just download the three packages above from your favorite
+Debian mirror. If you do that, you need to extract them yourself, which
+is done with the following command:
+
+dpkg-source -x xfree86_4.1.0-16.dsc &lt;only for manual download&gt;
+
+Either method will give you a 'xfree86-4.1.0' directory in the download
+directory. For both this step, and the later package creation, you are
+going to need tools from the 'dpkg-dev' package. So install that with
+'apt-get' if you don't already have it installed.
+
+
+
+<B>2. Make sure you have the requisite build dependencies.</B>
+
+XFree86 requires certain packages to compile. It seems that 'dpkg' is
+pretty good about telling you what packages it needs if they are not
+installed (probably a nice script included by the Debian X maintainer).
+Thus, you can just try and compile it and see what it complains about
+not having and, at that point, install the needed packages with apt-get.
+The other, cleaner, option is to run:
+
+apt-get build-dep xfree86
+
+This command will attempt to automatically install (or once in a blue
+moon uninstall) all the necessary packages to compile XFree86. You need
+to be 'root' to do this.
+
+
+<B>3. Begin the Build Process for xfree86</B>
+
+
+Become 'root' (or you could use fakeroot), and change into the
+'xfree86-4.1.0' directory. Then run:
+
+dpkg-buildpackage
+
+The source will begin to compile. It will take a while. If you have a
+really old machine, like a Pentium 166 or such, go on a vacation. It
+may be done when you return.
+
+
+<B>4. Recompile the wacom_drv.o module.</B>
+
+The source you need to replace and recompile will be in the following
+directory:
+
+&lt;download_dir&gt;/xfree86-4.1.0/build-tree/xc/programs/Xserver/hw/xfree86/input/wacom
+
+Replace the 'xf86Wacom.c' file therein with the one provided. While X
+does need to have some configuration done for this to compile, and that
+configuration is accomplished automatically by the build process, it
+does not necessarily need to finish compiling the whole package. So,
+once a 'Makefile' is created automatically by the build scripts in this
+directory, you can attempt to run 'make' from time to time. Once it
+compiles without error, you can halt the very lengthy build process of
+the XFree86 package.
+</PRE>
+
+<BLOCKQUOTE><DIV CLASS=diff>This may not work with the current beta package
+since it also contains the wcm-beta.c and wcm-beta.h files. You can
+solve this by updating the Makefile, or proceed to step five
+on the <A HREF="#builddrv">Building wacom_drv.o From Scratch</A> page.
+The command line options for building the driver will undoubtably have
+changed, so be prepared to update the Makefile in the wacpack/src directory.
+</DIV></BLOCKQUOTE>
+
+<PRE>
+<B>5. Move the wacom_drv.o module to the proper place.</B>
+
+That's it. Once the 'make' command compiles without error, you will
+have several new files in the directory. The one we are interested in
+is 'wacom_drv.o.' Now you can just move the 'wacom_drv.o' file to the
+directory:
+
+/usr/X11R6/lib/modules/input/
+
+This will also require 'root' privileges.
+---------------------------------------------------------------------
+
+You may want to add that to you HOWTO, for my fellow Debian users. And
+thanks again for the very nice work on the HOWTO and the drivers.
+
+HTH,
+
+Tim
+</PRE>
+
+<CENTER><B>Copyright (C) 2002 - John E. Joganic</B></CENTER>
+</BODY>
+</HTML>
diff --git a/docs/docs.txt b/docs/docs.txt
new file mode 100644
index 0000000..8dd4cde
--- /dev/null
+++ b/docs/docs.txt
@@ -0,0 +1,2221 @@
+
+ John's Linux USB Wacom Page
+
+Last Modified: December 22, 2002 18:12
+Changelog <#changelog>
+
+
+Navigation: MAIN </wacom/index.php/id/book> NEXT
+</wacom/index.php/id/intro> INDEX </wacom/index.php/toc> *ALL*
+</wacom/index.php/all>
+
+
+
+ 1.0 - Introduction
+
+This document was written for three reasons: 1) to remind myself how I
+got my brand-new Wacom Intuos2 running on my Redhat 8.0 Linux box in
+case I have to do it again from scratch, 2) to help other people do the
+same, and 3) to help de-mystify the process by explaining why things
+work as they do, and where to look when things seem to be going wrong.
+
+If you find an error, have a question, or have something to add, please
+contact me directly at jej@j-arkadia.com
+<mailto:jej@j-arkadia.com?subject=WACOM>. Furthermore, please add the
+word "WACOM" to the subject line. I get hundreds of messages a day, so
+that'll help me see your message amidst the spam.
+
+
+ 1.1 - Expectations
+
+
+ What You Can Expect Today
+
+Depending on whether you are using serial or USB tablets, your
+experience will differ, but for the moment at least, the serial tablets
+have a generally simpler configuration. This document currently
+addresses only the USB tablets, but that will change in the future. Mr.
+Lepied's XInput driver for XFree86
+<http://people.mandrakesoft.com/~flepied/projects/wacom/> web page has a
+good description of the serial configuration process. The goal of this
+website is to make configuration and use of both tablet types as simple
+as possible. Here is what you can expect now:
+
+
+ You Should Be Able to Get Your Tablet Working
+
+Most all serial and USB tablets are working to a reasonably functional
+degree. I have an old ArtPad (1997) that does not initialize with the
+XFree86 driver, and USB Cintiq users are having a little trouble with
+the kernel drivers. Everyone else should be up and running, namely:
+
+ * PenPartner,
+ * Graphire,
+ * Graphire2,
+ * Intuos, and
+* Intuos2
+
+
+ All Your Fancy Styluses and Pointers Should Work
+
+I have a collection of pointers, pens, and brushes to test; all of them
+work completely as one would expect. Users have reported success with
+their tools as well. I have not tested the airbrush yet, but presumably
+it works just fine. Tools that are known to work include:
+
+ * Graphire2 2D mouse (EC-120-0K),
+ * Intuos Pen (GP-300E-01H),
+ * Intuos2 2D mouse,
+ * Intuos2 4D mouse,
+ * Intuos2 Lens cursor,
+ * Intuos2 grip pen (XP-501E-00A),
+ * Intuos2 stroke pen (XP-120-00A),
+* Intuos2 ink pen (XP-110-00A),
+
+
+ You Will Need to Modify Your XF86Config File
+
+This project is actively working to become a turn-key system. It will be
+awhile before major distributions like Redhat include these settings
+automatically for you. Until that time, you will need to make some
+changes to your XFree86 configuration files by hand.
+
+
+ You May Need to Update Your Kernel
+
+This project assumes that you have a kernel version of at least 2.4.18.
+While it may be possible to use this document with older kernels, the
+likelihood of success diminishes the farther back you go. Redhat 8.0
+ships with this kernel, and most of the major distributions are already
+past this point.
+
+
+ You Will Need to Work With the Command Line
+
+This document details a step-by-step process for setting up and
+diagnosing the tablet. There are no GUI tools to help you through this
+process- at least not today. Some familiarity with the command line is
+recommended since I provide enough information to get the task
+accomplished, but I do not explain how the command line itself operates.
+Any good book on UNIX will do, and I use the bash shell for all my work.
+If you are not using bash, you presumably know what you are doing already.
+
+
+ What You Can Expect Soon
+
+I have a near-term design goal to get all the tablets and their
+respective tools functioning using the original driver design. This
+currently includes the airbrush (which may work already), the Cintiq,
+and if possible and time permits, the lowly ArtPad. If there are any
+outstanding problems with the original driver, I will take care of them
+as they come up.
+
+While the current way of configuring the tablet is sub-par in my
+opinion, it is also the established way of doing things. Merging
+large-scale changes back into either the Linux kernel or the XFree86
+code tree will be difficult, if for no other reason than insufficient
+multi-platform testing. To solve this problem, the new functionality
+described below will be implemented in a separate project and accessed
+through the original driver as an advanced option. This will allow all
+the various platforms to use the new code or not depending on their own
+constraints and circumstances. It also allows for people to write their
+own tablet libraries without mucking around in the XFree86 code.
+
+
+ The Next Steps
+
+First and foremost, the tablet needs to be configurable without
+restarting X. By separating the tablet code from the low-level X code,
+it will be possible to talk directly to the tablet engine without
+interfering with the X server's operation. Additionally, it will be
+possible to use either the XInput configuration API or the tablet
+engine's API to access that functionality. The preferred way would
+probably be an X server extension, but today at least, that is outside
+of the scope of my interest. Ask me again in another month.
+
+The next issue is plug and play. The USB tablets have the unfriendly
+habit of being unaccessible when they are unplugged and replugged. Since
+the X server holds the device open, the tablets cannot reconnect to
+their original position and get bumped to another event slot. The new
+tablet engine will address the problem, and can probably even auto-probe
+for the USB tablets.
+
+Third, a GUI-based configuration tool should cap things off nicely. I
+intend to replicate the functionality of the Windows configuration tool.
+This includes diagnostics, pressure mappings, button settings, keyboard
+mappings, etc.
+
+
+ Your Role
+
+I have some expectations as well. If you have a Wacom tablet and you use
+this code, I would appreciate it if you could test the beta releases
+whenever a feature or issue that might affect you comes up. The sooner
+that problems are resolved, the less likely you will be impacted by a
+buggy production release. If you only use the stable builds, none of the
+new features will be available to you until after they have been
+thoroughly hammered out which could be awhile. Of course, nothing is
+stopping you from using the stable builds; they are purposefully made
+available for people and companies who need the tablet drivers as stable
+as possible.
+
+
+ 1.2 - Most Recent Updates
+
+This is a list of the updates for the past month or so. The Changelog
+<#changelog> has a more complete list.
+
+December 22, 2002 - Added support for the serial Intuos2 under
+xf86Wacom.c. Fixed the sign convention on the mouse wheel for USB Intuos
+mice. Graphire should be fixed, too. Added preliminary support for
+Volito to kernel driver. Cleaned up wacdump and hid-core to compile on
+Debian Woody without warnings. Added build help for Debian Stable users.
+Added ability to read serial devices using wacdump. New package is
+0.3.3-beta.
+
+December 15, 2002 - Added initial framework for new driver code. Fixed
+the mouse wheel for 4D mice. Fairly responsive, almost too responsive,
+in fact. (Updated: quick fix to xf86Wacom.c to remove some debugging
+code that got left in accidentally; new driver version is 0.3.2a, but
+the package is the same.)
+
+December 13, 2002 - Added development page. New beta build has fix for
+2D mouse wheel. Wacdump displays relative data now. Build environment
+for wacom_drv.o set up for new code.
+
+December 9, 2002 - Added more information about the parameters accepted
+by the InputDevice section, including the man pages.
+
+November 24, 2002 - Moved beta tree to version 0.2.0. Added complete
+configure-based environment. wacom.o and wacdump are built by default;
+wacom_drv.o is available in binary and source form.
+
+
+ 1.3 - Success Stories
+
+Originally, this document dealt only with Redhat 8.0. I will extend this
+to cover as many different distributions as possible, but I can
+personally only test on machines at my disposal. At present, that means
+Redhat 7.2 and 8.0. If you have success stories for a distribution or
+version not mentioned, let me know so I can update the document.
+
+ * Redhat
+ o Redhat 8.0 - tested on kernel 2.4.18-18.8.0
+ o Redhat 8.0 - tested on kernel 2.4.18-17.8.0
+ o Redhat 7.3 - tested on kernel 2.4.18-17.7.x
+ o Redhat 7.2 - tested on kernel 2.4.18-17.7.x
+ * Mandrake
+ o Mandrake 9.0 - tested on ???
+ * Gentoo
+ o Gentoo 1.4rc - tested on 2.4.19 kernel, gcc3.2, XFree86 4.2.0
+ * Debian
+ o Debian Woody - XFree86 4.2 on 2.4.20
+ o Debian Stable - XFree86 4.1 (see Building wacom_drv.o On
+ Debian Stable <#debwcmdrv>)
+ * Slackware
+ o Slackware 8.1, (standard release)
+ * SuSE
+ o Suse Linux 8.0, kernel 2.4.18, XFree 4.2
+
+
+ 1.4 - How To Use This Document
+
+ Where the guide differs between distributions or packages, I have
+ added a comment like this to help distinguish between versions.
+
+In terms of document organization, if you have not figured it out
+already, you can browse the document one page at a time, or you can
+click on the ALL link and view the entire thing in one long page.
+
+Next, this document was written with the assumption that you are
+starting from scratch with a relatively recent distribution of the Linux
+kernel. Also, if you have already added lines to your X11 configuration
+file (XF86Config), you should comment them out and restart X. Since
+we'll be stepping through the entire process, we need X to ignore the
+tablet until we're ready. Otherwise, X will just get in the way.
+
+Finally, if you know what you're doing, you can leave your X settings
+intact, print this out, switch to runlevel 3, and follow along from the
+console. The newer packages should have an HTML and text version of this
+document in the documentation directory. And of course, there's always
+lynx <http://lynx.isc.org/> for the gurus out there, which is why this
+document is written out in plain vanilla HTML.
+
+
+ 1.5 - Resources and References
+
+Lepied's Wacom XInput driver for XFree86
+<http://people.mandrakesoft.com/~flepied/projects/wacom/> This page
+contains the original driver and help for serial tablet users.
+Lepied's Wacom Mailing List Archive
+<http://www.lepied.com/archive_wacom.html> This is an archive of
+Lepied's driver mailing list. /Warning: posting directly to the mailing
+list page does not mail to the list. Many posts are missed this way./
+Subscribe to Lepied's Wacom Mailing List
+<mailto:wacom-subscribe@lepied.com> If you want to post to the list and
+receive notifications about driver updates, this link will subscribe you.
+Stefan Runkel's Wacom Tablet HOWTO
+<http://www.runkel-it.de/wacom_tablet_howto.html> This page is a bit
+old, but it covers a lot of details that I mention only briefly. Good
+reading, even if it is possibly outdated.
+Wacom Webpage <http://www.wacom.com> The Wacom web site contains little
+information about the Linux drivers, but you can look at their nifty new
+tablets.
+pxh: Graphire USB <http://www.pxh.de/fs/graphire/> This page is devoted
+to a Graphire-specific XFree86 driver.
+A Webpage Dealing with Cintiq (Wacom PL-500 in particular) on Linux
+<http://homepages.feis.herts.ac.uk/~bt7al/wcm/> This website deals with
+getting the Cintiq running; I will be merging this code into the kernel
+soon.
+Wacom Intuos USB Micro-HOWTO <http://sem.best.vwh.net/linux/wacom/> An
+older webpage which may not be relevent anymore, but was useful to me
+when I got started.
+Wacom Intuos USB on Linux
+<http://www.ecn.wfu.edu/~cottrell/thinkpad/tpa21m/wacom.html> Another
+page that was useful to me when I started.
+Wacom Software Developer Support
+<http://www.wacomeng.com/devsupport/index.html> This page contains some
+documentation for the older tablets.
+
+
+ 1.6 - Frequently Asked Questions
+
+This FAQ is based on the questions that I receive in email. If you have
+a question that is not answered here, send me email directly.
+<mailto:jej@j-arkadia.com?subject=WACOM> Please add the word "WACOM" to
+the subject line to help me distinguish your message from the spam I
+normally receive.
+
+
+ Index
+
+ * Who is responsible for this code? <#WHO>
+ * Is my tablet supported? What about my special stylus? <#SUPPORT>
+ * What about my Penpartner/Graphire/Cintiq? <#OTHERS>
+ * How do I get my stylus to stop clicking randomly whenever I use
+ it? <#SPURIOUS>
+ * The old drivers had filtering, how do I enable it now? <#FILTER>
+ * What happened to feature XYZ? <#FEATURE>
+* How do I get the special features of my stylus to work? <#BUSTED>
+
+
+
+
+ Who is responsible for this code?
+
+We are. That is, Linux users with Wacom tablets are responsible for the
+development of this code. The source code was written by at least two
+dozen individuals, none of whom still seem to be directly involved in
+the project. I, John Joganic, took up the code in November of 2002,
+largely to satisfy my own needs, but also because I believe Linux needs
+to be more useful in the graphic and video industries. What I ask of
+you, is to use the drivers and report the problems you encounter.
+
+
+ Is my tablet supported? What about my special stylus?
+
+Most recent tablets are supported as well all the styluses. The
+Expectations <#expect> page has a list of all the tablets and styluses
+that are known to work as well as what you can expect out of these
+drivers now and in the future.
+
+
+ What about my Penpartner/Graphire/Cintiq?
+
+The USB kernel driver currently seems to support Penpartner, Graphire,
+Graphire2, Intuos, Intuos2, and PL400-800. Based on user reports, the
+Cintiqs may identify themselves as PL series tablets. There is no kernel
+driver necessary for serial tablets. The XFree86 wacom driver supports
+both USB and serial devices. It has support for all the USB devices as
+well as virtually all of the older tablet models as well. Depending on
+how the Cintiq identifies itself, it may potentially work with the
+XFree86 driver as a serial device. There are no guarantees as of yet, so
+I would not buy one (currently over $3000 USD) on the assumption that it
+will work without doing some actual research.
+
+
+ How do I get my stylus to stop clicking randomly whenever I use it?
+
+The "Threshold" setting in the InputDevice section of the XFree86
+configuration file determines the minimum pressure value before a touch
+event is detected. Wacdump can show you what pressure values are
+actually being received from the tablet, and setting the threshold to a
+value higher than that should solve the problem. My stylus oscillates
+between 0 and 6, even if the stylus is not touching the tablet. I use a
+threshold of 10.
+
+
+ The old drivers had filtering, how do I enable it now?
+
+At some point, the maintainers of the wacom kernel driver ripped out all
+the good features and moved them into the xf86Wacom driver. Not
+everything works as well as one might like. The "Suppress" setting in
+the InputDevice section specifies the tablet sensitivity.
+
+
+ What happened to feature XYZ?
+
+As mentioned above, many of the features in the original kernel driver
+were removed and transferred to the xf86Wacom driver. Not all the
+features made the trip. The goal of this website is to have all the
+original features working again as well as all the features available
+for the windows driver. If your favorite feature is not working, drop me
+a line. I prioritize my work based on my perception of user need.
+
+
+ How do I get the special features of my stylus to work?
+
+There are many different types of stylus, mice, and cursors. All of them
+work to varying degrees, so if you are running into trouble, send me
+email. There are presently known issues with the mouse wheel on 2D mice
+(fixed in 0.3.1-beta), and some of the extra buttons on the 4D mice.
+While the values are generally reported correctly to the driver, the
+xf86Wacom driver does not interpret the information properly. The 4D
+mouse wheel for instance behaves completely contrary to expectation
+(fixed in 0.3.2-beta). These problems will be addressed in future
+production releases (and are available now in the beta releases).
+
+I do not have an airbrush tool at my disposal. If you have one and it
+does not work for you, let me know. You could be beta-testing the code
+that gets this working.
+
+
+ 1.7 - Development
+
+
+ CURRENT DEVELOPMENT
+
+Date: 2002-12-22 (Sunday)
+Status: Moving over to SourceForge, and testing new serial driver.
+Estimated Time: Ready on 2002-12-29 (Sunday)
+
+
+ Completed
+
+ * Added Intuos2 serial to xf86Wacom.c.
+ * Added serial tablet code to wacdump.c.
+ * Added experimental Volito code to kernel.
+ * Split ncurses and Linux input code to avoid namespace collision on
+ Debian.
+* Corrected wheel sign for USB mice.
+
+
+ Immediate
+
+ * Get code onto SourceForge.
+* Intuos2 2D mouse does not work correctly under xf86Wacom.c
+
+
+ Secondary
+
+ * Getting test application for XF86 driver together
+ * Testing ability to create new pointer devices on the fly
+* Testing ability to fork out new server process
+
+
+
+
+ KNOWN ISSUES
+
+There are plenty of known issues. If you have one, and it is not listed,
+check the FAQ, and if it's not there either, let me know.
+
+
+ wacom.o
+
+ * 2002-12-13: Tablet does not reinitialize properly when replugged
+(report #1).
+
+
+ wacdump
+
+* Nothing outstanding.
+
+
+ wacom_drv.o
+
+ * 2002-12-22: Intuos2 2D mouse does not work correctly under
+xf86Wacom.c
+
+
+ 1.8 - Wacom Driver Theory of Operation
+
+Initially at least, the Wacom tablet is an HID compliant device, and
+when first connected to the computer, will identify itself as such.
+Unfortunately, this is not what you want because in this mode, you will
+not get any of the fancy features. The hid-core.c and usbmouse.c files
+contain exceptions for the wacom; when the device is detected, they
+ignore the tablet. In this way, the more sophisticated wacom driver has
+the opportunity to assume control.
+
+The first thing that the driver does is register itself with the USB
+subsystem and wait for work to do. When the user plugs the device in, or
+the device is first detected, the USB subsystem shops the vendor and
+device identifier around, checking it against different drivers. The
+wacom driver takes responsibility for the tablet and then notifies the
+event system that it will be providing data. It then asks the tablet to
+switch from HID-compliant mode to "mode 2", a wacom-specific protocol
+which allows for values like pressure, Z rotation, and tilt. As
+information arrives, the wacom driver dutifully converts the data into
+real-world values and hands it on to the event system.
+
+From here, any usermode application can get access to the event data by
+opening /dev/input/event0. A stream of events including mouse movements,
+clicks, and proximity updates can be read from the device. One such
+application that would be interested in this data is the X system
+itself. An XInput driver (wacom_drv.o) collects that information,
+applies various forms of filtering, and offers it to GUI-based
+applications throught the XInput API. A graphics program like gimp can
+then get access to the positions of various tools by querying X directly.
+
+By breaking the responsibility for the data into three distinct levels,
+we keep the kernel code simple and robust, the applications generalized,
+and the fancy features commonly accessible to all GUI applications in
+the X window system itself. This document walks down the entire data
+path from the kernel driver to the gimp application.
+
+
+ 2.0 - Getting It Together
+
+This section is devoted to preparing your system for the installation.
+In some cases, your distribution may have automatically loaded certain
+modules which you will now need to unload. Incidentally, some
+distributions detect new hardware on boot. If you allow Redhat's
+"anaconda" for instance to automatically configure (or remove) your
+tablet, it may undo some of the settings you will make here. Until
+Redhat's installation program recognizes Wacom tablets as non-HID
+devices by default, you are best off in my opinion to /not/ allow it to
+configure the device. Just a warning.
+
+
+ 2.1 - Before We Start
+
+From the beginning, let's make certain that we are on the same page.
+First, unplug the USB Wacom. If you have Wacom related lines in your
+XF86Config file, you should comment them out or remove them; then restart X.
+
+Unload the old modules.
+
+[jej@ayukawa jej]$ su -
+[root@ayukawa root]# lsmod | grep wacom
+*wacom* 8020 0 (unused)
+input 5920 0 [*evdev* *wacom* mousedev keybdev hid]
+usbcore 77024 1 [*wacom* usb-storage snd printer hid usb-uhci]
+[root@ayukawa root]# rmmod wacom evdev
+
+The kernel now knows nothing about the Wacom or how to handle
+event-based input devices. If you get an error unloading (busy driver
+perhaps), try rebooting.
+
+Check the presence of the input devices in the /dev/input directory. You
+should find approximately 114 devices with names such as event*, mouse*,
+keyboard, js*, and ttyACM*. We are interested in two devices, mouse0 and
+event0.
+
+[root@ayukawa root]# cd /dev/input
+[root@ayukawa input]# ls -la mouse0 event0
+crw------- 1 root root 13, 64 Aug 30 16:31 event0
+crw------- 1 root root 13, 32 Aug 30 16:31 mouse0
+[root@ayukawa input]# xxd mouse0
+xxd: mouse0: No such device
+[root@ayukawa input]# xxd event0
+xxd: event0: No such device
+
+On some distributions, Mandrake 9.0 for instance, the /dev directory is
+managed by devfs; Redhat 8.0 does not use it. When devfs is running, the
+/dev/input directory and/or its contents may be missing if the driver is
+not loaded. If you are not using devfs and the /dev/input directory or
+mouse0/event0 devices are not present, your distribution may be lacking
+these devices. This could be an oversight, or it could be an indication
+that your distribution is too old.
+
+If after executing /xxd mouse0/ you *did not* receive the "xxd: mouse0:
+No such device" message, then the input device is still loaded; go back
+and try running /rmmod evdev/ again. Once the new drivers are compiled
+and installed, this command will stream information from the Wacom
+tablet. mouse0 behaves like a PS/2 style mouse. event0 provides
+extensive information about the tablet including position, tilt,
+pressure, and buttons. It can also track at least two different tools
+simultaneously (Intuos2).
+
+
+ 2.2 - Downloading the Code
+
+The file wacpack-0.2.0.tar.gz </wacom/wacpack-0.2.0.tar.gz> is the
+stable package and contains files that you will need to get the tablet
+working. The current beta package wacpack-0.3.3-beta.tar.gz
+</wacom/wacpack-0.3.3-beta.tar.gz> is also available and may be used by
+people who are willing to risk an occasional kernel panic to help test
+new features. I will never put a beta package on this site that I am not
+running myself on my primary development machine, so you can be certain
+that if there are any obvious show stoppers, they will be fixed before
+you get to see them.
+
+
+ Stable files included for wacpack-0.2.0:
+
+File Comment
+configure - configure script for distribution independent builds
+prebuilt/wacom_drv.o - binary X11 driver for the wacom (26-j0.2.0)
+src/wacom.c - a stable, working kernel driver (1.30-j0.2.0)
+src/wacdump.c - a simple program for displaying tablet event data
+directly using ncurses; current version is 0.2.0
+src/usbmouse.c - replacement kernel driver (2.4.19), use only if needed
+src/hid-core.c - replacement kernel driver (2.4.19), use only if needed
+src/hiddev.c - replacement kernel driver (2.4.19), use only if needed
+src/hid-input.c - replacement kernel driver (2.4.19), use only if needed
+src/xf86Wacom.c - source for wacom_drv.o; requires XF86 build
+environment to compile. Binary available in prebuilt directory.
+GPL - the GNU General Public License, in case you did not already have
+twenty or more lying around
+
+
+ Beta files included for wacpack-0.3.2-beta:
+
+File Comment
+configure - configure script for distribution independent builds
+prebuilt/wacom_drv.o - binary X11 driver for the wacom (26-j0.3.2a)
+src/wacom.c - a stable, working kernel driver (1.30-j0.3.1)
+src/wacdump.c - a simple program for displaying tablet event data
+directly using ncurses; current version is 0.3.2
+src/usbmouse.c - replacement kernel driver (2.4.19), use only if needed
+src/hid-core.c - replacement kernel driver (2.4.19), use only if needed
+src/hiddev.c - replacement kernel driver (2.4.19), use only if needed
+src/hid-input.c - replacement kernel driver (2.4.19), use only if needed
+src/xf86Wacom.c - legacy source for wacom_drv.o; requires XF86 build
+environment to compile.
+src/wcm-beta.c - beta source for wacom_drv.o; requires XF86 build
+environment to compile.
+src/wcm-beta.h - beta source for wacom_drv.o; requires XF86 build
+environment to compile.
+GPL - the GNU General Public License, in case you did not already have
+twenty or more lying around
+
+
+ Stable Packages by Version and Date:
+
+File Date Comment
+wacpack-0.2.0.tar.gz </wacom/wacpack-0.2.0.tar.gz> - 2002-11-24
+Configure-based package
+wacpack-0.1b.tar.gz </wacom/wacpack-0.1b.tar.gz> - 2002-11-06
+wacpack-0.1a.tar.gz </wacom/wacpack-0.1a.tar.gz> - 2002-11-05
+wacpack-0.1.tar.gz </wacom/wacpack-0.1.tar.gz> - 2002-11-04
+
+
+ Beta Packages by Version and Date:
+
+File Date Comment
+wacpack-0.3.3-beta.tar.gz </wacom/wacpack-0.3.3-beta.tar.gz> -
+2002-12-22 Serial Intuos2, serial wacdump, Volito added, USB mouse wheel
+direction fixed, kernel 2.4.20 friendly
+wacpack-0.3.2-beta.tar.gz </wacom/wacpack-0.3.2-beta.tar.gz> -
+2002-12-15 Fix for 4D mice and working beta driver shell
+wacpack-0.3.1-beta.tar.gz </wacom/wacpack-0.3.1-beta.tar.gz> -
+2002-12-13 Fix for 2D mice and relative events in wacdump
+wacpack-0.3.0-beta.tar.gz </wacom/wacpack-0.3.0-beta.tar.gz> -
+2002-11-24 Configure-based package
+wacpack-0.1g-beta.tar.gz </wacom/wacpack-0.1g-beta.tar.gz> - 2002-11-14
+wacdump updated; displays ranges, cleaner output
+wacpack-0.1f-beta.tar.gz </wacom/wacpack-0.1f-beta.tar.gz> - 2002-11-14
+build environment for xf86Wacom added
+wacpack-0.1e-beta.tar.gz </wacom/wacpack-0.1e-beta.tar.gz> - 2002-11-11
+fixed range for I2-6x8
+wacpack-0.1d-beta.tar.gz </wacom/wacpack-0.1d-beta.tar.gz> - 2002-11-10
+updated wacdump, fixed range for I2-12x12
+wacpack-0.1c-beta.tar.gz </wacom/wacpack-0.1c-beta.tar.gz> - 2002-11-09
+updates to range, pressure, and mouse events
+
+
+ 2.3 - Configuring the Package
+
+Versions of wacpack-0.2.0 and greater are based on GNU's configure
+script based build environment. The upside is that more details are
+guessed by default. The downside is that the default configuration
+builds only the kernel driver and wacdump programs (which should cover
+almost everybody), but if you need a different module built, you will
+have to reconfigure the package first. This is mostly because different
+modules have different dependencies, some of which are difficult to
+resolve. A list of the options currently available in the stable package
+is presented below. Remember that for every --enable option, there is
+also an equivalent --disable option.
+
+Option Default Builds
+--enable-wacom *yes* wacom.o kernel driver
+--enable-wacdump *yes* wacdump event monitor
+--enable-hid no hid.o replacement kernel driver (not normally necessary)
+--enable-usbmouse no usbmouse.o replacement kernel driver (not normally
+necessary)
+--enable-wacomdrv no wacom_drv.o XFree86 driver (binary is available in
+prebuilt directory)
+--enable-modver best guess enables kernel module versioning; usually
+guesses correctly, but can be enabled or disabled if not
+--with-kernel=dir best guess Specifies the kernel source directory if
+configure cannot guess correctly.
+--with-xf86=dir none Specifies the XFree86 build directory.
+--with-arch=arch best guess Specifies the architecture if configure
+guesses incorrectly.
+
+Chances are high that you will be able to run the configure script
+without any options and have it execute correctly. Here is a sample
+output of the script on a Redhat 8.0 system:
+
+[jej@ayukawa wacpack]$ ./configure
+checking for a BSD-compatible install... /usr/bin/install -c
+checking whether build environment is sane... yes
+...
+checking for processor type... *i686*
+checking for kernel module versioning... *yes*
+checking for kernel sources... */usr/src/linux-2.4*
+...
+
+----------------------------------------
+ BUILD ENVIRONMENT:
+ architecture - i686
+ module versioning - yes
+ kernel - yes
+ XFree86 - no
+
+ BUILD OPTIONS:
+ wacom.o - yes
+ wacdump - yes
+ hid.o - no
+ usbmouse.o - no
+ wacom_drv.o - no
+----------------------------------------
+
+The processor type is determined by the script and used to build the
+kernel modules. Similarily, the script attempts to discover if the
+kernel is using module versioning by looking for version numbers in the
+usb.o module of the currently active kernel. This may not work well on
+all distributions, so if you get a line like "checking for kernel module
+versioning... confused" or "maybe; assuming yes", you'll have to
+manually enable or disable the option if it guesses incorrectly.
+
+The kernel source directory is assumed to be either /usr/src/linux-2.4
+or /usr/src/linux. If your kernel sources are elsewhere, you will need
+to specify the directory with the --with-kernel option.
+
+Generally, you will not need to build wacom_drv.o since it ships in
+binary form in the prebuilt directory. It is compiled with glibc with
+gcc 3.2 on a Redhat 8.0 system; if this will not work for you, building
+from source may be your only option. See the Building wacom_drv.o From
+Scratch <#builddrv> page for more information.
+
+The configure script is pretty simple, so if you find any problems and
+are inclined to fix them, please send me your patch so I can include the
+change in future releases.
+
+The following sample command-line will build everything but wacdump
+while disabling module versioning:
+
+[jej@ayukawa wacpack]$ ./configure --enable-hid --enable-usbmouse \
+ --enable-wacomdrv --with-xf86=/usr/src/redhat/BUILD/XFree86-4.2.0 \
+ --disable-modver --disable-wacdump
+checking for a BSD-compatible install... /usr/bin/install -c
+checking whether build environment is sane... yes
+...
+checking for processor type... i686
+checking for kernel module versioning... *yes*
+checking for kernel sources... /usr/src/linux-2.4
+checking for valid XFree86 build environment... *ok*
+...
+----------------------------------------
+ BUILD ENVIRONMENT:
+ architecture - i686
+ module versioning - *no*
+ kernel - yes
+ XFree86 - *yes*
+
+ BUILD OPTIONS:
+ wacom.o - yes
+ wacdump - *no*
+ hid.o - *yes*
+ usbmouse.o - *yes*
+ wacom_drv.o - *yes*
+----------------------------------------
+
+Notice that the configure script guessed module versioning was enabled
+by default, but was disabled by the command-line option
+--disable-modver. Similarly, the wacdump program which is enabled by
+default was also disabled. All the kernel modules and the XFree86 wacom
+driver are enabled.
+
+If the configure script fails to find something that it is looking for,
+it may disable some options that you previously enabled on the
+command-line. If this happens, check the output for a warning like the
+following:
+
+***
+*** WARNING:
+*** Unable to compile wacom_drv.o without XF86 build environment
+*** wacom_drv.o will not be built
+***
+
+In this particular case, the XFree86 driver was enabled, but the
+--with-xf86 option was not specified. Without the build environment, the
+module cannot be compiled and was consequently disabled.
+
+
+ 3.0 - The Kernel Driver
+
+The kernel driver for the Wacom Intuos that ships with the Linux kernel
+through 2.4.19, and in particular Redhat 8.0, does not work correctly.
+The bug is pretty simple and you can fix it yourself. Optionally, you
+can compile the version that I provide directly and swap the new module
+for the old one. This avoids having to rebuild the entire kernel.
+
+Kernel modules must be recompiled for each new kernel so I can't just
+provide binaries. By the time you read this, my present kernel will be
+entirely out of date with yours. Additionally, this fix will appear in
+2.4.20, so until you upgrade your kernel to that point, each time you
+upgrade to a version less than that, you will need to recompile this
+driver and swap it for the old one. In many cases, distributions like
+Redhat will "back-port" the fixes to older kernels, so keep an eye out
+for the change. Your driver may already be fixed.
+
+
+ 3.1 - Updated wacom.c
+
+The wacom.c driver that is available in wacpack-0.2.0.tar.gz
+</wacom/wacpack-0.2.0.tar.gz> has a number of updates which cannot be
+found in many distributions yet.
+
+First and foremost, it fixes an error that shipped with the 2.4.19
+kernel and was subsequently fixed in 2.4.20. This bug fix was made
+available in the Redhat 8.0 back-ported kernel version 2.4.18-18.x.y.
+Other distributions may not yet be updated.
+
+This driver contains several changes provided by Wacom UNIX developer
+Ping Cheng. Items include revised pressure code, better handling of 2D
+mice and lens cursors.
+
+The driver also contains additional debugging code which may be useful
+in diagnosing data flow problems of which there are many, unfortunately.
+
+
+ 3.2 - Building wacom.c
+
+Building the kernel driver requires having the kernel source. If you are
+running on Redhat, you can get it by installing the kernel-source RPM.
+At the time of this writing, I have the following RPMs installed:
+
+ kernel-2.4.18-18.8.0
+ kernel-2.4.18-14
+ kernel-source-2.4.18-18.8.0
+
+On a Redhat 8.0 machine, the kernel source is installed to
+/usr/src/linux-2.4. You will need to know where your source is installed
+to continue with the next step. After unpacking the wacpack-0.2.0.tar.gz
+</wacom/wacpack-0.2.0.tar.gz> file, configure the package. If your
+kernel is not located at either /usr/src/linux-2.4 or /usr/src/linux,
+use the --with-kernel option as shown below. Otherwise, just run
+/configure/ without any options.
+
+[jej@ayukawa wacpack]$ ./configure --with-kernel=/home/jej/src/linux
+...
+checking for valid kernel source tree... *ok*
+...
+----------------------------------------
+ BUILD ENVIRONMENT:
+ architecture - i686
+ module versioning - yes
+ kernel - *yes*
+ XFree86 - no
+
+ BUILD OPTIONS:
+ wacom.o - yes
+ wacdump - yes
+ hid.o - no
+ usbmouse.o - no
+ wacom_drv.o - no
+----------------------------------------
+
+If you did not receive an error or warning and the BUILD ENVIRONMENT
+summary shows "kernel - yes" then your kernel source tree was detected.
+If not, then you will need to look at the error or warning and determine
+the correct source directory. Now is also a good time to verify that the
+architecture was correctly detected. The --with-arch option allows you
+to change the value.
+
+Another detail that you need to be concerned about is module versioning.
+If it reads "yes" or "no" then the configure script has determined the
+correct setting. If it says "unknown," then it will default disabled,
+and you may have to enable it manually with --enable-modver. If module
+versioning is disable when it should be enabled, depmod will complain
+about missing symbols. If it is enabled when it should be disabled, the
+code may not compile, and it almost certainly will not load properly. If
+in doubt, leave it disabled for now and enable it later if depmod
+complains.
+
+To build the driver, just run /make/. The output will be a file called
+wacom.o in the src directory. This is your replacement driver.
+
+
+ 3.3 - Testing If wacom.o Will Load
+
+Before we install the wacom driver, we need to test that it will load
+properly. We do this by loading the driver manually. WARNING: there is a
+small chance that this will bomb your kernel, so we run /sync/ to write
+all the stale buffers to the disk. People using ext3 have little to
+worry about, but it's always good to be prepared for the worst. At the
+very least, save your work.
+
+[jej@ayukawa wacom]# su -
+[root@ayukawa root]# cd /home/jej/src/wacom/src
+[root@ayukawa src]# sync
+[root@ayukawa src]# rmmod wacom
+[root@ayukawa src]# insmod wacom.o # for those about to rock, we salute you.
+
+Well, if you did not bomb, then good. And if you did, well, sorry. So
+far, we have not had any reports of this happening, so please send in
+yours.
+
+If you get errors inserting the module, then you may need to reconfigure
+and build with module versioning disabled. If it loads without a hitch,
+move on to the next part.
+
+
+ 3.4 - Installing wacom.o
+
+To install or not to install, that is the question. Since the driver is
+in memory, you can pretty much use it this way throughout the rest of
+this document. Anywhere you see /modprobe wacom/, you'll instead need to
+/insmod wacom.o/. You'll also need to be careful that you use the
+correct path to wacom.o since insmod will load the driver from the
+kernel modules directory if it fails to find the module you specified.
+The result is that you'll be using the wrong driver.
+
+Why would you not install the driver? Well, for one, you may be using a
+beta driver, and if the system crashes (you get an Oops or things come
+unglued in other ways), it would be nice to reboot and have the original
+drivers load instead.
+
+When should I install the driver? When you're comfortable that the
+driver will not crash your system. The wacpack tarballs will be marked
+as beta if I am not absolutely convinced that they are stable. On the
+other hand, the new features will be in beta tarballs before they are
+marked stable, so there you have it. For the first time through this
+document, I would recommend installing the files found in the stable
+tarball. If you really know what you're doing, just load the drivers
+manually like in the previous section Testing If wacom.o Will Load
+<#testwacom>.
+
+On some distributions, Mandrake included, the wacom.o driver that
+appears in the kernel modules directory appears to be compressed. If you
+cannot find wacom.o using the method below, try locating wacom.o.gz
+instead. People who encountered this problem were able to run gzip on
+the module and copy that instead.
+
+Installing the driver requires knowing where it belongs. A little
+research will help here. By using the /locate/ command, you can find all
+copies of the original driver on the computer.
+
+jej@ayukawa wacom]$ locate wacom.o
+/lib/modules/2.4.18-14/kernel/drivers/usb/*wacom.o*
+/lib/modules/2.4.18-18.8.0/kernel/drivers/usb/*wacom.o*
+
+[jej@ayukawa wacom]$ uname -r
+*2.4.18-18.8.0*
+
+On this computer, there are two kernels installed. /uname/ identifies
+the currently active kernel as 2.4.18-18.8.0. The correct driver to
+replace is therefore at
+/lib/modules/2.4.18-18.8.0/kernel/drivers/usb/wacom.o. You will need to
+be root to replace this file, and it is a very good idea to make a
+backup copy.
+
+[jej@ayukawa wacom]$ su -
+[jej@ayukawa root]# cd /lib/modules/2.4.18-18.8.0/kernel/drivers/usb
+[jej@ayukawa usb]# cp *wacom.o* /home/jej/src/wacom/*wacom_old.o*
+[jej@ayukawa usb]# cp /home/jej/src/wacom/*wacom.o* *wacom.o*
+
+Here, I've saved the original to wacom_old.o and copied my new driver
+over it. Substitute directory names as appropriate.
+
+Finally, it is always a good thing to update the module dependencies.
+This is where you find out if the module was compiled without kernel
+module versioning. The following command, even if it generates errors is
+relatively benign. If it fails, then there is no harm done. It just
+means that you will have to load modules in the correct order since the
+system will not be able to guess for you.
+
+[jej@ayukawa usb]# depmod -e
+
+If you get no errors and no output, everything is fine, and the module
+was compiled, linked, and installed properly. If you received unresolved
+symbols like usb_set_idle or printk, then you need to reconfigure with
+module versioning enabled and recompile. Or not. As I said, it is not
+critical.
+
+
+ 3.5 - Loading the wacom Driver
+
+If you have installed the driver, now is the time to test whether it
+will load when needed. If you have not installed it, but are instead
+using insmod, substitute /insmod mydir/wacom/wacom.o/ where you see
+/modprobe wacom/ below. It is important that you use the correct wacom.o
+file, the one you just built, since insmod may load the old driver if it
+cannot find the one you have specified.
+
+I am assuming that you are using a USB device, so you will also need to
+modprobe for usb-uhci. The important features of the stylus are
+available through the Linux event drivers, so load those too. With some
+clever additions to /etc/modules.conf, you can probably have these
+automatically loading for you whenever the tablet is activated. So far,
+I haven't figured out how to make this work reliably, so I've added the
+commands to my /etc/rc.d/rc.local file.
+
+ On Redhat 7.2 and possibly on other older distributions, mousedev
+ and input are not loaded by default. Without them, the wacom driver
+ may fail to load, due to unresolved symbols, and the mouse driver
+ may not receive wacom mouse events, even if loaded afterwards.
+
+[jej@ayukawa usb]# rmmod wacom
+[jej@ayukawa usb]# modprobe usb-uhci
+[jej@ayukawa usb]# modprobe input
+[jej@ayukawa usb]# modprobe mousedev
+[jej@ayukawa usb]# modprobe wacom
+[jej@ayukawa usb]# modprobe evdev
+
+Check the system log for status messages pertaining to the wacom. Here's
+a copy of the messages from my version of the driver.
+
+[jej@ayukawa usb]# grep -i wacom /var/log/messages | tail
+Dec 22 21:23:35 ayukawa kernel: usb.c: registered new driver wacom
+Dec 22 21:23:35 ayukawa kernel: wacom.c: *v1.30-j0.2.0* Vojtech Pavlik <vojtech@suse.cz>
+Dec 22 21:23:35 ayukawa kernel: wacom.c: USB Wacom Graphire and Wacom Intuos tablet driver (MODIFIED)
+
+The original driver was version 1.21.3. This version number is
+1.30-j0.2.0 so the correct driver was loaded.
+
+
+ 3.6 - Testing Tablet Detection
+
+In this section we will plug the tablet into the computer and determine
+which driver, if any, claims control over the tablet. There are at least
+three drivers that are interested: 1) hid.o which may think it is an HID
+device, 2) usbmouse.o which may think it is an HID mouse, and 3) the
+wacom driver which should identify the tablet as its own. Any number of
+problems may be experienced here, so be prepared to spend some time
+looking at the logs. A good way to do this, if you are running X, is to
+open a separate xterm, su to root, and run /tail -f /var/log/messages/.
+The console window will stream anything that is appended to the log.
+
+
+ 3.6.1 - Plug It In
+
+Test the tablet detection by plugging the wacom into the USB port and
+checking /var/log/messages again. You should see a flurry of activity.
+The exact output depends a lot on your particular kernel and distribution.
+
+This is Redhat 8.0 (2.4.18-17.8.0):
+
+[jej@ayukawa usb]# tail /var/log/messages
+Dec 22 21:26:11 ayukawa kernel: hub.c: USB new device connect on bus2/2, assigned device number 2
+Dec 22 21:26:11 ayukawa kernel: *input0: Wacom Intuos2 12x12* on usb2:2.0
+Dec 22 21:26:14 ayukawa /etc/hotplug/usb.agent: Setup wacom hid for USB product 56a/44/115
+Dec 22 21:26:14 ayukawa /etc/hotplug/usb.agent: Setup *mousedev* for USB product 56a/44/115
+
+And here it is again on Redhat 7.2 (2.4.18-17.7.x):
+
+[jej@sasami root]# tail /var/log/messages
+Dec 22 21:28:38 sasami kernel: hub.c: USB new device connect on bus1/1, assigned device number 2
+Dec 22 21:28:38 sasami kernel: *input0: Wacom Intuos2 12x12* on usb1:2.0
+Dec 22 21:28:39 sasami kernel: usb.c: registered new driver hiddev
+Dec 22 21:28:39 sasami kernel: usb.c: registered new driver hid
+Dec 22 21:28:39 sasami kernel: hid-core.c: v1.8.1 Andreas Gal, Vojtech Pavlik <vojtech@suse.cz>
+Dec 22 21:28:39 sasami kernel: hid-core.c: USB HID support drivers
+Dec 22 21:28:39 sasami kernel: mice: *PS/2 mouse* device common for all mice
+
+If all went well like above, the USB device was successfully detected
+and handled by the wacom driver; secondly, it was assigned input0. This
+presumably means that information like pressure and tilt will be
+received on /dev/input/event0. Lastly, the tablet was setup with
+mousedev or the generic PS/2 USB mouse driver, so that information
+should appear on /dev/input/mouse0.
+
+If instead you got any of the following lines in your log, the wacom
+driver /did not get control/. Either hid or usbmouse did.
+
+ *input0,hiddev0: USB HID v1.00 Mouse* [Tablet XD-0608-U] on usb1:5.0
+ *input0: Tablet XD-1212-U* on usb2:2.0
+
+The next section describes what to do if the wacom driver did not get
+control. Otherwise, skip on to Viewing the Raw Data <#viewdata>.
+
+
+ 3.6.2 - Wacom Driver Does Not Get Control
+
+If the wacom driver did not get control, and instead you see "Tablet
+XD-1212-U" or similar in its place, then this section is for you.
+Otherwise, consider yourself lucky and skip on to the next section,
+Viewing the Raw Data <#viewdata>. For the unlucky, there is a good
+possibility that your kernel is slightly out of date and that your
+usbmouse.c and hid-core.c files need some help.
+
+When the Tablet XD-1212-U driver comes up, either usbmouse.o or hid.o
+has discovered your tablet, decided it was an HID device, and grabbed
+it. This is bad news because HID tablets seem to only output relative
+coordinates and nothing about pressure. You are in good company though
+because a number of people have reported this, even on some fairly
+up-to-date distributions. Now let's fix it.
+
+
+ 3.6.3 - Building usbmouse.o and hid.o
+
+In the wacpack-0.2.0.tar.gz </wacom/wacpack-0.2.0.tar.gz> file, you will
+find the 2.4.19 versions of usbmouse.c and hid-core.c files which have
+special exceptions for wacom. These files are not built by default, so
+you will need to reconfigure the package and run make again.
+
+At least one person has reported errors building these files on Debian
+Woody running kernel version 2.4.20. This is not surprising since the
+source code came from 2.4.19, but I will do what I can to get these
+drivers updated in the near future.
+
+[jej@ayukawa wacom]$ ./configure --enable-hid --enable-usbmouse
+...
+ BUILD OPTIONS:
+ hid.o - *yes*
+ usbmouse.o - *yes*
+...
+[jej@ayukawa wacom]$ make
+[jej@ayukawa wacom]$ cd src
+[jej@ayukawa src]$ ls -la hid.o usbmouse.o
+-rw-rw-r-- 1 jej jej 26590 Dec 22 18:56 hid.o
+-rw-rw-r-- 1 jej jej 4848 Dec 22 18:56 usbmouse.o
+
+First off, if the code does not build, and you cannot chase the problem
+down yourself, send me a line. If your kernel is much older than 2.4.18,
+then you may have to resign yourself to the fact that the 2.4.19 drivers
+are not going to work for you. I would recommend upgrading your kernel.
+Some people are already running kernel version 2.4.20 and have
+encountered problems building as well. I will update these drivers to
+2.4.20 as soon as possible.
+
+Rather than copy these files to the kernel modules directory right away,
+it would be wise to test whether they get the job done. We should also
+verify that they load properly.
+
+[jej@ayukawa src]$ su -
+[root@ayukawa root]# cd /home/jej/src/wacom/src
+[root@ayukawa src]# rmmod hid usbmouse wacom
+[root@ayukawa src]# insmod hid.o
+[root@ayukawa src]# insmod usbmouse.o
+[root@ayukawa src]# insmod wacom.o
+
+As for unloading the old drivers, none of them should be busy, but if
+you run into trouble, you can use /lsmod/ to check the dependencies and
+try again. Now let's check how the wacom driver managed:
+
+[jej@ayukawa wacom]# tail -15 /var/log/messages
+Dec 22 21:07:38 ayukawa kernel: usbmouse.c: *mouse_init (MODIFIED DEBUG)*
+Dec 22 21:07:38 ayukawa kernel: usb.c: registered new driver usb_mouse
+Dec 22 21:07:39 ayukawa kernel: hid-core.c: *hid_init (MODIFIED DEBUG)*
+Dec 22 21:07:39 ayukawa kernel: usb.c: registered new driver hiddev
+Dec 22 21:07:39 ayukawa kernel: usb.c: registered new driver hid
+Dec 22 21:07:44 ayukawa kernel: usb.c: registered new driver wacom
+Dec 22 21:07:44 ayukawa kernel: usbmouse.c: *usb_mouse_probe: ignoring wacom*
+Dec 22 21:07:44 ayukawa kernel: input0: *Wacom Intuos2 12x12* on usb2:8.0
+Dec 22 21:07:44 ayukawa kernel: wacom.c: v1.30-j0.2.0 Vojtech Pavlik <vojtech@suse.cz>
+Dec 22 21:07:44 ayukawa kernel: wacom.c: USB Wacom Graphire and Wacom Intuos tablet driver *(MODIFIED-DEBUG)*
+
+If everything is working correctly, the log should look similar to the
+lines above. I removed some of the extraneous information, so if you
+have more than this, you should be fine. The most important thing here
+is that the usb_mouse_probe detected and ignored the wacom. It actually
+gets several shots at it, but I removed the redundant lines. For the
+final test, unplug and replug the tablet. Now check the log. The wacom
+driver should be calling the shots from here on out.
+
+If all is well, and you are installing modules rather than /insmod/'ing
+them as you go, then now is the time to copy the usbmouse.o and hid.o
+drivers to their proper places in the kernel modules directory. As
+before with some other distributions, if the files in the kernel module
+directory are compressed, you'll need to run gzip on the .o files to get
+.o.gz files. Use /locate/ as you did for the wacom.o file if necessary
+and don't forget to backup the originals.
+
+
+ 3.6.4 - More about hid.o
+
+Incidentally, the hid.o file may not be necessary, but I've included the
+source to build it anyway because hid-core.c contains a lot of wacom
+related code. If the hid.o driver gets called upon to control the wacom,
+and it is compiled in debug mode, you may see a line similar to the
+following in the log:
+
+Dec 22 21:11:44 ayukawa kernel: usb_hid_configure: configuring device 56A.44 (WACOM), quirk is 4 (IGNORE)
+
+If you get this message, but with a different quirk, or if the wacom
+driver still is not getting control by this point, there is a
+possibility that your tablet is not listed in any of the driver files.
+If so, continue on to the next section. Otherwise, if you are up and
+running, skip to Viewing the Raw Data <#viewdata>.
+
+
+ 3.6.5 - Unknown Tablet?
+
+To determine whether your device is listed in the driver, we need to
+determine the device identifier. It can be discovered by doing the
+following:
+
+[root@ayukawa root]# grep -i 56a /var/log/messages | tail -10
+Dec 22 21:03:09 ayukawa /etc/hotplug/usb.agent: Setup mousedev for USB product *56a/44/115*
+Dec 22 21:27:48 ayukawa kernel: usb.c: USB device 2 (vend/prod *0x56a/0x44*) is not claimed by any active driver.
+
+In this case, the tablet identifier is 56a/44 (also written 0x56a/0x44).
+The model is determined by the second number; the Intuos2 12x12 is 0x44
+for instance.
+
+In the wacom.c file, you will find a table called "wacom_ids." Look for
+your device identifier. Only the identifiers listed are handled by the
+wacom driver, so if it is missing, it needs to be added. Also look in
+hid-core.c. It has a table called "hid_blacklist" which identifies
+devices to ignore. The code is a little strange, but PENPARTNER is 0x00,
+GRAPHIRE is 0x10, INTUOS is 0x20, PL is 0x30, and INTUOS2 is 0x40. A
+table entry like INTUOS2 + 4 would therefore be 0x44.
+
+If you've gotten this far, and still cannot get it to work, send me
+email with your device identifier and as much of an explanation of where
+things did and did not work as described. I'll see what I can do about
+at least finding out why it did not work. Then we can go on to solutions.
+
+The next section assumes you have things working up to this point.
+
+
+ 3.7 - Viewing the Raw Data
+
+View the raw data from the tablet, by returning to the /dev/input
+directory and streaming the data directly from the device. Be patient
+here because this is where a lot of people are getting stuck.
+
+[root@ayukawa usb]# cd /dev/input
+[root@ayukawa input]# xxd event0
+0000000: e65d c33d 597d 0100 0100 4101 0100 0000 .].=Y}....A.....
+0000010: e65d c33d 5c7d 0100 0400 0000 b701 2800 .].=\}........(.
+0000020: e65d c33d d9bb 0100 0100 4101 0000 0000 .].=......A.....
+0000030: e65d c33d dcbb 0100 0400 0000 b701 2800 .].=..........(.
+(Ctrl-C)
+
+First off, you have to move the mouse or tap the pen to get any output.
+Second, you might not get anything at all. Don't panic. This seems to
+happen occasionally. Unfortunately, the work-around is neither
+straightfoward nor foolproof. I am currently talking with Wacom
+developers about this and hopefully we'll get it solved soon.
+
+If absolutely no output occurs, you should check your messages file for
+the following line:
+
+[jej@sasami root]# tail /var/log/messages
+Dec 22 17:31:06 sasami kernel: *wacom_intuos_irq: received unknown report #1*
+Dec 22 17:31:09 sasami last message repeated 195 times
+
+This is a common failure mode of the driver, and I am investigated the
+cause. Each time this has happened to me though, I /have/ been able to
+get it to work. Simply unload and reload the driver.
+
+[jej@sasami root]# rmmod wacom
+[jej@sasami root]# modprobe wacom
+[jej@sasami root]# tail /var/log/messages
+Dec 22 17:31:31 sasami kernel: usb.c: deregistering driver wacom
+Dec 22 17:31:34 sasami kernel: usb.c: registered new driver wacom
+Dec 22 17:31:35 sasami kernel: input0: Wacom Intuos2 12x12 on usb1:2.0
+Dec 22 17:31:35 sasami kernel: wacom.c: v1.30-j0.2.0 Vojtech Pavlik <vojtech@suse.cz>
+
+Apparently, the device driver and the tablet occassionally get out of
+sync with the tablet thinking it's still in HID mode when in fact it
+should be in "mode 2." By unloading and reloading the driver manually,
+the initialization code has another opportunity to get it right. Try the
+/xxd /dev/input/event0/ again. This time, it /should/ work. If not, send
+me some email. I am trying to create a repeatable test case for the
+Wacom developers, and your problem may be just what we need to get this
+bug fixed.
+
+Incidentally, if you have a program running that is connected to
+/dev/input/event0 (like X or wacdump for instance), it is possible that
+the tablet will not reattach back to the same event. I have seen the
+wacom reattach to /dev/input/event1 when unloading and reloading the
+wacom driver with wacdump running for instance.
+
+You should also try running /xxd/ on /dev/input/mouse0. You should get
+streams of data when the mouse and pen are moved around the surface of
+the tablet. It is this device that X will look at for mouse movement.
+Use Ctrl-C to exit xxd.
+
+
+ 4.0 - wacdump
+
+wacdump.c parses and displays the raw event output. You must be root to
+run it unless you've set the permissions on /dev/input/event0 such that
+you can read it. This program can run simultaneously with X, but it's
+best if X has not be configured for the tablet yet. wacdump is enabled
+by default in the configure script, so it should be built automatically
+when you run /make/.
+
+The command line usage of wacdump (wacpack-0.2.0) is pretty simple:
+
+Usage: wacdump [-d device]
+ -?, -h, --help - usage
+ -d, --device device - use specified device
+
+If you know that the tablet is attached to a device other than
+/dev/input/event0, you can override it with the -d option, but
+otherwise, the default is correct. If you connect the /dev/input/event0
+and get an end-of-file error, then the wacom is probably attached to a
+different event.
+
+When you run wacdump, nothing will happen until you place a mouse or pen
+near the surface. After that, the screen will clear and you will see a
+screen similar to the following output:
+
+Wacom Intuos2 12x12 bus=3, vndr=56A, prd=44, ver=115
+
+ KEY=00000000.00000000 0000 0000 00000000
+ ABS=00000000.00000000 0000 0000 00000000
+ MSC=3DD488B5.0000F385 0004 0000 0028102E
+
+ X=+17623 (+00000 .. +30480) Y=+10730 (+00000 .. +31680)
+ RZ=-00058 (-00900 .. +00899) THROTTLE=+00000 (-01023 .. +01023)
+ WHEEL=+00000 (+00000 .. +01023) PRESSURE=+00005 (+00000 .. +01023)
+DISTANCE=+00007 (+00000 .. +00015) TILT_X=+00094 (+00000 .. +00127)
+ TILT_Y=+00122 (+00000 .. +00127)
+
+ WHEEL=+00000
+ SERIAL=0028102E
+
+ LEFT= RIGHT= MIDDLE= SIDE=
+ EXTRA= PEN= RUBBER= BRUSH=
+ PENCIL= AIR= MOUSE= LENS=
+ TOUCH= STYLUS= STYLUS2=
+
+This output comes from version 0.3.2 of the wacdump program.
+
+The first row displays the model number, vendor id, product id, and
+revision number.
+
+The next block shows events as they arrive. Only the event types that
+the device claims to generate are displayed. The quickly changing
+numbers are timestamps.
+
+The middle block shows absolute positions of various device parameters.
+It also shows the ranges that the device claims to provide. If your
+device exceeds these ranges, please let me know so I can fix the driver.
+All the available positions are displayed, even though some of them are
+not available for certain pointers. Different tablets will have
+different capabilities; these are capabilities registered specifically
+for your tablet by the wacom.o kernel driver.
+
+The second WHEEL value is from the relative events channel. Presently,
+only the beta drivers report relative events.
+
+The serial number of the current stylus is displayed next. When two
+pointers are present, the serial number will alternate. This is
+especially true of Intuos2 models and possibly less accurate of other
+models.
+
+Lastly, the button positions are displayed. Some mice have more buttons
+than others; the ones displayed are the ones that the tablet offers.
+Buttons like "Pen" or "Airbrush" are displayed as "Down" when the tool
+is in proximity to the surface.
+
+Whenever you are curious if the device is working properly, wacdump is a
+useful tool for determine exactly what is coming from the tablet. You
+can run it in conjunction with X or without. The display is constrained
+to 80x25 so it can also be used on the main console terminal.
+
+
+ 5.0 - Configuring X11
+
+Two steps must be completed to get X to recognize the Wacom. First, you
+need to add some lines to XF86Config to inform X of the tablet's
+existence. Second, you need to update the XInput driver that pertains to
+the Wacom since the one that ships with XFree86 is not very functional.
+Neither driver holds a candle to the windows driver though, so you'll
+have to take what you get for the time being. Updates to the XFree86
+driver are available in the stable and beta releases on the Downloading
+the Code <#download> page. You may also check Lepied's Wacom Site
+<http://people.mandrakesoft.com/~flepied/projects/wacom/> which has
+source code up to version 26 as of this writing.
+
+
+ 5.1 - Adding the InputDevices
+
+The X Window system identifies the stylus and eraser on your tablet as
+XInput devices. Applications that want to know the absolute position of
+your stylus can request that information directly, and this generally
+bypasses whatever the mouse happens to be doing at the time. This design
+is not without problems though. For one, changing your setup or adding a
+new tool requires making changes to the XF86Config file and restarting
+X. This should be fixed in the future.
+
+For now however, add the InputDevice sections to your XF86Config file.
+This assumes you are running XFree86 4.x. On some distributions, this
+file is called XF86Config-4.
+
+Section "InputDevice"
+ Driver "wacom"
+ Identifier "cursor"
+ Option "Device" "/dev/input/event0"
+ Option "Type" "cursor"
+ Option "Mode" "relative"
+ Option "USB" "on"
+ Option "Speed" "3.0"
+ *Option "Threshold" "10"*
+EndSection
+
+Section "InputDevice"
+ Driver "wacom"
+ Identifier "stylus"
+ Option "Device" "/dev/input/event0"
+ Option "Type" "stylus"
+ Option "Mode" "absolute"
+ Option "USB" "on"
+ Option "Tilt" "on"
+ #Option "TiltInvert" "on"
+ *Option "Threshold" "10"*
+EndSection
+
+Section "InputDevice"
+ Driver "wacom"
+ Identifier "eraser"
+ Option "Device" "/dev/input/event0"
+ Option "Type" "eraser"
+ Option "Mode" "absolute"
+ Option "USB" "on"
+ Option "Tilt" "on"
+ #Option "TiltInvert" "on"
+ *Option "Threshold" "10"*
+EndSection
+
+#Section "InputDevice"
+# Driver "wacom"
+# Identifier "tablet"
+# Option "Device" "/dev/input/event0"
+# Option "Type" "beta"
+#EndSection
+
+The first three sections identify the cursor, stylus, and eraser devices
+to XInput. Notice that all three reference /dev/input/event0.
+Additionally, the TiltInvert option has been commented out. You may want
+to experiment with these values.
+
+The fourth InputDevice section is for the new beta driver. Unless you
+are actually testing this code, I do not recommend using this section,
+at least not at present. There is no harm in leaving it in the file,
+commented in or out, just so long as it is not used in the ServerLayout
+section described later.
+
+*The threshold value is very important.* Various wacom tablets behave
+differently in terms of how the "TOUCH" event is detected. On some
+tablets, the device itself makes the determination, and on others, the
+driver detects the event by examining the pressure value. To make
+matters more complex, the ranges are all different so what is good for
+my Intous2 may not be good for your Graphire. I am providing a default
+value of 10. If the pressure exceeds that value, a "TOUCH" event is sent
+to the XInput system. If this is too hard, you may want to reduce it. If
+the value is too low, you will encounter the reviled "spurious touch"
+problem where the stylus clicks randomly as you move it around the screen.
+
+Also, people using serial tablets will need completely different
+settings. Please see Lepied's Wacom Site
+<http://people.mandrakesoft.com/~flepied/projects/wacom/> for more details.
+
+I am including a copy of the man page for the driver. Some of the fields
+pertain only to the serial device, and some of the fields may be
+deprecated under XFree86 4.2.0. Try them out, see what happens.
+
+WACOM(4x) XFree86 WACOM(4x)
+
+NAME
+ wacom - Wacom input driver
+
+SYNOPSIS
+ Section "InputDevice"
+ Identifier "idevname"
+ Driver "wacom"
+ Option "Device" "devpath"
+ ...
+ EndSection
+
+DESCRIPTION
+ wacom is an XFree86 input driver for Wacom devices.
+
+ The wacom driver functions as a pointer input device, and may be used
+ as the X server's core pointer.
+
+SUPPORTED HARDWARE
+ This driver supports the Wacom IV and Wacom V protocols. Preliminary
+ support is available for USB devices on some Linux platforms.
+
+CONFIGURATION DETAILS
+ Please refer to XF86Config(5x) for general configuration details and
+ for options that can be used with all input drivers. This section only
+ covers configuration details specific to this driver.
+
+ Multiple instances of the Wacom devices can cohabit. It can be useful
+ to define multiple devices with different active zones. Each device
+ supports the following entries:
+
+ Option "Type" "stylus"|"eraser"|"cursor"
+ sets the type of tool the device represent. This option is
+ mandatory.
+
+ Option "Device" "path"
+ sets the path to the special file which represents serial
+ line where the tablet is plugged. You have to specify it
+ for each subsection with the same value if you want to have
+ multiple devices with the same tablet. This option is
+ mandatory.
+
+ Option "USB" "on"
+ tells the driver to dialog with the tablet the USB way.
+ This option is only available on some Linux platforms.
+
+ Option "DeviceName" "name"
+ sets the name of the X device.
+
+ Option "Suppress" "Inumber"
+ sets the position increment under which not to transmit
+ coordinates. This entry must be specified only in the
+ first Wacom subsection if you have multiple devices for one
+ tablet. If you donâ??t specify this entry, the default value
+ is computed to
+
+ Option "Mode" "Relative"|"Absolute"
+ sets the mode of the device.
+
+ Option "Tilt" "on"
+ enables tilt report if your tablet supports it (ROM version
+ 1.4 and above). If this is enabled, multiple devices at
+ the same time will not be reported.
+
+ Option "HistorySize" "number"
+ sets the motion history size. By default the value is zero.
+
+ Option "AlwaysCore" "on"
+ enables the sharing of the core pointer. When this feature
+ is enabled, the device will take control of the core
+ pointer (and thus will emit core events) and at the same
+ time will be able, when asked so, to report extended
+ events. You can use the last available integer feedback to
+ control this feature. When the value of the feedback is
+ zero, the feature is disabled. The feature is enabled for
+ any other value.
+
+ Option "TopX" "number"
+ X coordinate of the top corner of the active zone.
+
+ Option "TopY" "number"
+ Y coordinate of the top corner of the active zone.
+
+ Option "BottomX" "Inumber"
+ X coordinate of the bottom corner of the active zone.
+
+ Option "BottomY" "number"
+ Y coordinate of the bottom corner of the active zone.
+
+ Option "KeepShape" "on"
+ When this option is enabled, the active zone begins
+ according to TopX and TopY. The bottom corner is adjusted
+ to keep the ratio width/height of the active zone the same
+ as the screen while maximizing the area described by TopX,
+ TopY, BottomX, BottomY.
+
+
+ Option "DebugLevel" number
+ sets the level of debugging info reported.
+
+ Option "BaudRate" "38400", "19200" or "9600" (default)
+ changes the serial link speed. This option is only availâ??
+ able for wacom V models (Intuos).
+
+ Option "Serial" "number"
+ sets the serial number associated with the physical device.
+ This allows to have multiple devices of the same type (i.e.
+ multiple pens). This option is only available on wacom V
+ devices (Intuos). To see which serial number belongs to a
+ device, you have to set the DebugLevel to 6 and watch the
+ output of the X server.
+
+ Option "Threshold" "number"
+ sets the pressure threshold used to generate a button 1
+ events of stylus devices for some models of tablets (Intuos
+ and Graphire).
+
+SEE ALSO
+ XFree86(1), XF86Config(5x), xf86config(1), Xserver(1), X(7x).
+
+AUTHORS
+ Frederic Lepied <lepied@xfree86.org>
+
+4.2.0 Version WACOM(4x)
+
+
+ 5.2 - Mouse1
+
+Adding the Mouse1 device is /probably not something you want to do/, and
+Redhat's Anaconda program will do it for you if you boot the machine
+with the tablet plugged in. You'll need to be careful about this.
+
+When you use the mouse1 input device, the data flows from the wacom
+kernel driver, through the event subsystem, down into the mousedev
+driver, out the /dev/input/mouse0 device, and finally into the XInput
+mouse driver. You effectively lose all your absolute positioning
+information because the mousedev driver converts it into relative data.
+Additionally, the XFree86 wacom driver does not get control of the
+cursor because mouse1 is providing those events.
+
+Therefore, if you have a Mouse1 section, leave it. Redhat 8.0 at least,
+expects it to be there; however, you will not be using it, so make
+certain that it is commented out of the section covered next.
+
+
+ 5.3 - ServerLayout
+
+The ServerLayout section describes what devices the X server will use.
+Modify the ServerLayout section to reflect the new devices. Make certain
+to comment out the Mouse1 device.
+
+Section "ServerLayout"
+ Screen 0 "Screen0" 0 0
+ InputDevice "Mouse0" "CorePointer"
+ InputDevice "Keyboard0" "CoreKeyboard"
+ *InputDevice "cursor" "SendCoreEvents"
+ InputDevice "stylus" "SendCoreEvents"
+ InputDevice "eraser" "SendCoreEvents"*
+ #InputDevice "Mouse1" "SendCoreEvents"
+ #InputDevice "tablet" "SendCoreEvents"
+EndSection
+
+This section determines which devices are actually used by the server.
+In the case above, the cursor, stylus, and eraser devices are selected
+while the mouse1 and tablet devices are commented out. At present, this
+is the correct configuration for proper operation of the tablet whether
+you are using the beta package or the production package.
+
+The new wacom_drv.o driver contains two independent implementations of
+the tablet code. Using the "tablet" device instead of the
+cursor/stylus/eraser devices will enable that code. Presently, that will
+do little for you since the code is fairly volatile and is simply a
+testbed for various design ideas. As it becomes more useful, I will
+update this section.
+
+You have completed the XF86Config file changes. By aware that if you
+reboot your computer with the Wacom plugged in and Redhat's Anaconda
+program notices, it will treat the tablet as a USB mouse and reconfigure
+this file incorrectly. You may need to go back and check the file to
+ensure that everything is still correct afterwards. Rebooting with the
+device detached seems to reverse the process, but again, you should
+check the file to be certain. My recommendation is to tell Anaconda to
+ignore the tablet until the device detection works properly.
+
+
+ 5.4 - Updating wacom_drv.o
+
+There are a number of wacom_drv.o files about. XFree86 is shipping
+version 23. Redhat 8.0 is shipping version 25. Lepied's page has the
+source code for 26, but the binary identifies itself as 23ss1. The
+Downloading the Code <#download> page currently offers source and binary
+for 26-j0.2.0 in the stable package and 26-j0.3.2a in the beta version.
+In total, I recommend using the drivers in the packages that I provide
+since they correspond with what is shown in this document. Any new
+functionality that gets added will be built on top of these files as well.
+
+Note: the binary wacom_drv.o file is located in the prebuilt directory
+of the stable and beta packages. Building this file from source (which
+is provided) is a major hassle, and I do not recommend it unless you are
+developer yourself or the binary is simply not compatible with your
+system. Instructions for rebuilding from source are available in the
+Appendix under Building wacom_drv.o From Scratch <#builddrv>.
+
+With that said, locate and replace wacom_drv.o. Using /locate/ again, we
+find the driver in the X11 tree. This directory is pretty standard, so
+it is unlikely that your file will be located elsewhere. Note the
+assumption is that you are using XFree86 4.x. Anything from the 3.x
+version probably won't work.
+
+[root@ayukawa root]# locate wacom_drv.o
+/usr/X11R6/lib/modules/input/*wacom_drv.o*
+[root@ayukawa root]# cd /usr/X11R6/lib/modules/input
+[root@ayukawa input]# cp *wacom_drv.o* /home/jej/src/wacom/*wacom_drv_old.o*
+[root@ayukawa input]# cp /home/jej/src/wacom/prebuilt/*wacom_drv.o wacom_drv.o*
+
+Again, the old file is copied away, and replaced with the newer version.
+The binary modules are presently built on a Redhat 8.0 system using gcc
+3.2 under glibc. This should work for most, if not all, major
+distributions. If it does not load on yours, please let me know.
+
+
+ 5.5 - Restart X
+
+Finally, restart X. You may wish to do this from runlevel 3 for testing
+purposes.
+
+[root@ayukawa root]# init 3
+...processes starting and stopping...
+[root@ayukawa root]# startx
+
+If the X server dies, you can always back-out the changes to the
+XF86Config file and try again. Worse case, copy the wacom_drv_old.o file
+back to its original place. But first, look at the XFree86 log file for
+clues. You might want to do this even if everything works correctly.
+When things are running right, the following lines appear in my log file.
+
+[root@ayukawa root]# grep -i wacom /var/log/XFree86.0.log
+(II) LoadModule: "wacom"
+(II) Loading /usr/X11R6/lib/modules/input/wacom_drv.o
+(II) Module wacom: vendor="The XFree86 Project"
+(II) Wacom driver level: *26-j0.2.0* $
+(II) XINPUT: Adding extended input device "eraser" (type: Wacom Eraser)
+(II) XINPUT: Adding extended input device "stylus" (type: Wacom Stylus)
+(II) XINPUT: Adding extended input device "cursor" (type: Wacom Cursor)
+(==) Wacom Kernel Input device name: "Wacom Intuos2 12x12"
+(==) Wacom tablet maximum X=30480 maximum Y=30480 X resolution=0 Y resolution=0
+suppress=0
+(==) Wacom Cursor top X=0 top Y=0 bottom X=30480 bottom Y=30480
+(==) Wacom Stylus top X=0 top Y=0 bottom X=30480 bottom Y=30480
+(==) Wacom Eraser top X=0 top Y=0 bottom X=30480 bottom Y=30480
+
+Notice the driver version 26-j0.2.0 above. This is the new stable
+wacom_drv.o driver. The beta driver is currently 26-j0.3.2a.
+
+First things first, you should lift the mouse off the tablet and place
+it back down. This seems to help reset things internally. When you
+replace the mouse, the cursor should jump to that portion of the screen.
+If everything is working correctly, the mouse should work in absolute
+mode with the four corners of the tablet corresponding with the four
+corners of the screen. If the cursor stops short of an edge, then the
+kernel driver limits are probably incorrect. Please let me know so that
+I can update the driver.
+
+Next, remove the mouse and try using the eraser end of the stylus.
+Hovering over the surface of the tablet should move the cursor. Touching
+the eraser tip to the surface should generate a click. If you invert the
+pen and use the stylus tip, you should get a similar result. If the pen
+tip generates spurious touch events, you will need to increase the
+threshold value in the InputDevice section. Wacdump is useful for
+determining the appropriate value. My pen pressure oscillates between 0
+and 6 so I use a threshold of 10 to compensate.
+
+
+ 5.6 - Check the Pointer Status
+
+You can check the XInput pointer status by using /xsetpointer/ as below.
+The man page states that calling xsetpointer with the name of a
+particular device will set it as the primary pointing device. So far,
+that has not been the case in my experience; however, that is the
+subject of current development, so try it, and if it doesn't work, stay
+tuned.
+
+[root@ayukawa log]# xsetpointer -l
+"eraser" [XExtensionDevice]
+"stylus" [XExtensionDevice]
+"cursor" [XExtensionDevice]
+"Mouse0" [XPointer]
+"keyboard" [XKeyboard]
+
+
+ 6.0 - Working With Gimp
+
+It has been suggested that gimp should be recompiled from source
+(v1.2.3) on Mandrake 9.0. This does not seem to be true for Redhat 8.0.
+
+Bring up /gimp/ and select "File|Dialogs|Input Devices". You will see a
+drop-down list with all three devices present. After enabling them, you
+can see their respective statuses by calling up "File|Dialogs|Device
+Status". It has been recommended that the devices be set to "Screen"
+mode rather than "Window."
+
+I have successfully been able to use gimp with several different pens,
+including the tips and erasers. Tilt does not appear to be used by gimp
+at present, but pressure works fine.
+
+
+ 7.0 - Changelog
+
+ * 2002-12-22 - John E. Joganic <jej@j-arkadia.com>
+
+ * Added serial code to wacdump
+ * Added serial protocols IV 1.0 - 1.4.
+ * Added serial protocol V 1.0 - 1.1.
+ * Intuos mouse wheels are now positive when rolled backwards
+ per PS/2 Intellimouse convention
+ * Added Intuos2 to xf86Wacom.c
+ * Added build help for Debian Stable users
+
+ * 2002-12-17 - John E. Joganic <jej@j-arkadia.com>
+
+ * Merged code from 2.4.20 hid-core.c
+ * Added configuration code to handle kernels still at 2.4.19
+ * Split wacdump curses code into wacscrn.c to solve namespace
+ collision
+ * Added volito to wacom.c
+
+ * 2002-12-16 - John E. Joganic <jej@j-arkadia.com>
+
+ * House cleaning on the website.
+ * Dropped the Intuos constraint from the document name.
+ * Added the expectations page.
+ * Changed the internal indexing to identifier rather than
+ chapter.
+
+ * 2002-12-15 - John E. Joganic <jej@j-arkadia.com>
+
+ * Removed some debugging code, redeployed package with
+ wacom_drv.o version 0.3.2a.
+ * New build has fix for 4D mouse wheel.
+ * wcm-beta.c fleshed out with new driver code.
+
+ * 2002-12-13 - John E. Joganic <jej@j-arkadia.com>
+
+ * Added development page.
+ * New build has relative events and fix for 2D mouse wheel.
+ * wcm-beta.c added to build.
+
+ * 2002-12-09 - John E. Joganic <jej@j-arkadia.com>
+
+ * Added detail on "Threshold" parameter.
+ * Included man page.
+
+ * 2002-11-24 - John E. Joganic <jej@j-arkadia.com>
+
+ * Updated docs for version 0.2.0.
+ * Package uses configure-based scripts.
+
+ * 2002-11-14 - John E. Joganic <jej@j-arkadia.com>
+
+ * Moved wacom_drv.o to prebuilt directory.
+ * Added xf86Wacom.c (version 26) to package along with changes
+ to Makefile
+ * Extended documentation on X11 setup including caveat over
+ Mouse1 device.
+ * Added xf86Wacom.c build instructions.
+ * Updated wacdump (0.2) to display identifiers, maximum
+ values, and all registered event types.
+ * Added highlighting in wacdump to display button down state.
+
+ * 2002-11-11 - John E. Joganic <jej@j-arkadia.com>
+
+ * wacom.c - fixed ranges for intuos2 6x8
+
+ * 2002-11-10 - John E. Joganic <jej@j-arkadia.com>
+
+ * wacdump.c - updated display to identify values
+ * wacom.c - fixed ranges for intuos2 12x12
+
+ * 2002-11-09 - John E. Joganic <jej@j-arkadia.com>
+
+ * wacom.c - merged some of Cheng's changes, fixes to the
+ pressure calculation, device ranges.
+ * wacom.c - added an extra device (0x47) for M. T.
+ * wacdump.c - added text messages for event types
+ * wacdump.c - added usage and device override option
+ * wacdump.c - exits when channel is closed.
+ * Fixed module dependency problem in wacom.o and hid.o
+ * Added clarification for devfs and Mandrake 9.0
+
+ * 2002-11-06 - John E. Joganic <jej@j-arkadia.com>
+
+ * Busted page out into discrete chapters.
+ * Added some php magic to turn it into an active book.
+ * Added Mandrake 9.0
+ * Gimp may need to be compiled from source (1.2.3)
+
+ * 2002-11-05 - John E. Joganic <jej@j-arkadia.com>
+
+ * Updated wacom driver with Christer Nilsson's 2.4.20 patch
+ * Added RedHat 7.3 success
+ * Added more discussion on the device driver election.
+ * Added more discussion on HID mode 1 and the correct mode 2.
+ * Added usbmouse.c and hid-core.c to wacpack-0.1b.
+
+ * 2002-11-04 - John E. Joganic <jej@j-arkadia.com>
+
+ * Added update section.
+ * Added success stories.
+ * Added RedHat 7.2 specific information
+ * Added more information about "unknown report #1"
+
+
+ 8.0 - Contacts
+
+John Joganic can be contacted at the following email address:
+jej@j-arkadia.com <mailto: jej@j-arkadia.com?SUBJECT=WACOM>. If at all
+possible, please include the word "WACOM" in the subject line. I receive
+hundreds of messages a day, and this little addition will help me find
+your message in the tons of spam.
+
+
+ 9.0 - Appendix
+
+This section is for everything that is either not critical or simply too
+complex to describe in the document above without becoming overly
+distracting.
+
+
+ 9.1 - Building wacom_drv.o From Scratch
+
+
+ Introduction
+
+I should tell you out-right that this is an ugly, time consuming
+process. If you manage to get this working in fewer steps, by all means,
+let me know.
+
+Why would you want to do this? Two reasons. One, you are a developer and
+need to make changes to the source code directly. Two, your distribution
+uses c libraries or configuration options that are not compatible with
+the wacom_drv.o file that I provide. People running libc5 for instance,
+would need to build their own driver.
+
+ Timothy Klein has submitted a brief howto for compiling on Debian
+ Stable which is still running XFree86 4.1 as of this writing. It
+ covers steps one through four of this document, and a savvy
+ developer should be able to figure out step five on his own. If
+ someone solves step five and generates a patch to Makefile.am, I'll
+ see what I can do about getting it into the configuration script.
+ That document is on the Building wacom_drv.o On Debian Stable
+ <#debwcmdrv> page.
+
+You will need the X source code to rebuild the wacom_drv.o driver. The
+build configuration for X generates a number of header files that are
+necessary but not installed by default on most distributions.
+Consequently, you will need to not only get the source, but build it,
+practically in its entirety. Then, after all that, the configure script
+can be instructed to hook into the X build tree and rebuild xf86Wacom.c
+at any time without having to rebuild X again.
+
+Since I am running Redhat 8.0 and cannot really pull down the original
+XFree86 4.2.0 source code, compile it, and expect it to work on my
+system, I need to instead use the source RPM provided by Redhat. If you
+choose to go this route, I provide pretty detailed instructions for
+making this work. If your distribution works differently, or you are
+using Gentoo where most everything is source code by default, you'll
+need to handle this as best as possible according to your particular
+situation.
+
+
+ Step One: Get The Source
+
+On Redhat 8.0, I discovered the version number for my currently
+installed XFree86 packages by running /rpm -q XFree86/. This reported
+version 4.2.0-72, therefore the source package is
+XFree86-4.2.0-72.src.rpm. I downloaded the package from Redhat directly
+and installed it to the system as follows:
+
+[root@sen src]# rpm -ivh XFree86-4.2.0-72.src.rpm
+ 1:XFree86 ########################################### [100%]
+
+This installs a number of files to the /usr/src/redhat directory,
+particularly in the SOURCES and SPECS subdirectories. Other
+distributions undoubtedly install elsewhere. Look for the XFree86.spec
+file which should be located in the SPECS directory. This file contains
+all the information necessary to patch the orginal XFree86-4.2.0 source
+code to the level that Redhat is distributing in their regular binary
+package. The source code and patch files are located in SOURCES.
+
+
+ Step Two: Build the Source
+
+This step describes how to build the source from the RPM itself. If you
+are building from some other mechanism, I honestly cannot offer much
+assistance since I generally don't build my X system from scratch. If
+you'd like to write up a short section on building the server for your
+particular distribution, I would be happy to include it here.
+
+Next, you don't actually have to build the entire thing. The point at
+which the xf86Wacom.c driver can be built however, is not until
+somewhere in the middle of the build process. The driver depends on a
+number of header files that are created dynamically so until they are
+generated, wacom_drv.o cannot be compiled. My solution was to open a
+separate terminal in the wacom driver directory and periodically attempt
+to build it. When it successfully built, I stopped the X build process.
+Here's how to build the source for an RPM that's been exploded out into
+the SPECS and SOURCES directories.
+
+[root@sen root]# cd /usr/src/redhat
+[root@sen redhat]# rpmbuild -bc SPECS/XFree86.spec
+
+Not every distribution has /rpmbuild/; try using just /rpm/ instead. At
+some point, Redhat split the build functionality into separate programs.
+If after looking through the /rpm/ man page, you still cannot get this
+to work, send me some email, and I'll look into it.
+
+The important item is the "-bc" option of /rpmbuild/ which unpacks,
+patches, and builds the source without actually installing. While it is
+also possible to simply unpack and patch using the "-bp" option, there
+does not seem to be a way to just build. The "-bc" option simply deletes
+all the files provided by "-bp" and recreates them again. The downside
+of this is that if you wanted to simply unpack, patch, and then copy the
+new xf86Wacom.c file over the old one, you'll find that the build step
+deletes it and starts over again. I have gotten this to work by creating
+a new patch file, but this requires a bit more effort, so I don't
+recommend it right off.
+
+
+ Step Three: Build the Original Driver
+
+The xf86Wacom.c file is buried pretty deep in the X build tree. If it is
+in a different location than the one I have provided below, try using
+/find . -name xf86Wacom.c/ from the BUILD directory.
+
+[root@sen redhat]# cd BUILD/XFree86-4.2.0/xc/programs/Xserver/hw/xfree86/input/wacom
+[root@sen wacom]# ls
+Imakefile wacom.man xf86Wacom.c.Wacom-USB-driver-a25-update
+Makefile xf86Wacom.c
+
+The "a25-update" file is the original xf86Wacom.c file before Redhat's
+patch. If you open xf86Wacom.c, you'll find that it is version 25, at
+least as of this writing and this distribution. The presence of the
+Makefile means that the configuration has at least been run for this
+directory. If you have built a sufficient portion of the X source files,
+then all the header files that you need have been generated, and you can
+build xf86Wacom.c. Try it, and if it does not build, wait a bit. The
+absence of xf86Version.h for instance, is a good indication that the
+build process is not ready.
+
+[root@sen wacom]# make
+rm -f xf86Wacom.o
+gcc -O2 -march=i386 ... -c xf86Wacom.c
+rm -f wacom_drv.o
+ld -r xf86Wacom.o -o wacom_drv.o
+
+
+ Step Four: Build the New Driver
+
+With a valid build environment, it should be possible to copy the
+updated driver source over the old one, run /make/, and get an updated
+driver.
+
+ The beta package contains a version xf86Wacom.c that depends on
+ wcm-beta.c and wcm-beta.h. If you are compiling this version, you
+ will need to copy all three files. Secondly, the driver will build
+ and link, but will not be usable since the current X build
+ environment does not expect the wcm-beta code to be compiled. This
+ problem is addressed correctly in the automated build process
+ discussed below.
+
+[root@sen wacom]# cp xf86Wacom.c xf86Wacom_old.c
+[root@sen wacom]# cp /home/jej/src/wacom/src/xf86Wacom.c xf86Wacom.c
+[root@sen wacom]# make
+rm -f xf86Wacom.o
+gcc -O2 -march=i386 ... -c xf86Wacom.c
+rm -f wacom_drv.o
+ld -r xf86Wacom.o -o wacom_drv.o
+
+If the source did not build correctly the second time, it is possible
+that the Imake-based build environment has generated a makefile that
+will not work correctly for the new code. Your options are to generate a
+patch file that can be applied by the build process, hack the makefile
+in the driver directory to work properly, or to find some way to get
+Imake to reconfigure. This is probably less of a hassle for people
+building from source, but to be fair, all the commands and information
+you need to reconfigure from RPM are listed in the beginning of the
+build output. You might also want to continue ahead to see if the
+default build rule does not work for you.
+
+
+ Step Five: Automating the Build Process
+
+Copying the updated source file into the build tree and compiling from
+there is not convenient. By configuring the package with the --with-xf86
+option set to the XFree86 build tree and enabling the driver using the
+--enable-wacomdrv option, you can build the driver outside of the X
+build tree.
+
+[jej@ayukawa wacom]$ ./configure \
+ --with-xf86=/usr/src/redhat/BUILD/XFree86-4.2.0 \
+ --enable-wacomdrv
+...
+BUILD ENVIRONMENT:
+ XFree86 - yes
+ BUILD OPTIONS:
+ wacom_drv.o - yes
+[jej@ayukawa wacom]$ make
+
+The makefile rule which builds the driver is contained within
+src/Makefile.am and is modified according to the configuration to
+generate a rule similar to this in src/Makefile:
+
+xf86Wacom.o: xf86Wacom.c
+ gcc -O2 -march=i386 -mcpu=$(ARCHITECTURE) -pipe -ansi \
+ -pedantic -Wall -Wpointer-arith -fno-merge-constants \
+ -I. -I$(XF86_DIR)/programs/Xserver/hw/xfree86/common \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/loader \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/os-support \
+ -I$(XF86_DIR)/programs/Xserver/include \
+ -I$(XF86_DIR)/programs/Xserver/mi \
+ -I$(XF86_DIR)/exports/include/X11 \
+ -I$(XF86_DIR)/include/extensions \
+ -I$(XF86_DIR) \
+ -I$(XF86_DIR)/exports/include \
+ -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE \
+ -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE \
+ -DSHAPE -DXINPUT -DXKB -DLBX -DXAPPGROUP -DXCSECURITY \
+ -DTOGCUP -DXF86BIGFONT -DDPMSExtension -DPIXPRIV -DPANORAMIX \
+ -DRENDER -DGCCUSESGAS -DAVOID_GLYPHBLT -DPIXPRIV \
+ -DSINGLEDEPTH -DXFreeXDGA -DXvExtension -DXFree86LOADER \
+ -DXFree86Server -DXF86VIDMODE -DXvMCExtension \
+ -DSMART_SCHEDULE -DBUILDDEBUG -DXResExtension \
+ -DX_BYTE_ORDER=X_LITTLE_ENDIAN -DNDEBUG -DFUNCPROTO=15 \
+ -DNARROWPROTO -DIN_MODULE -DXFree86Module -DLINUX_INPUT \
+ -o xf86Wacom.o -c xf86Wacom.c
+
+ In the beta package, a similar rule applies to wcm-beta.c.
+
+The options and directories specified come directly from the output of
+the make command in the previous step. All the root and parent
+directories have been replaced with the macro XF86_DIR which in this
+case is set by the configuration script to
+/usr/src/redhat/BUILD/XFree86-4.2.0/xc. If the options that you see in
+your build are identical to those above, then the default rule will work
+for you now. If not, you'll need to make some alterations. You can
+update the Makefile.am file and rerun automake, update the Makefile.in
+and rerun configure, or just update the Makefile directly.
+
+So long as the X build tree exists, the include directories will point
+to the correct locations, and the driver will build. If space is an
+issue, you can probably remove all the non-essential directories, but be
+careful; the dependency tree in X is huge.
+
+
+ Conclusion
+
+Although a bit of a hassle, it is possible to compile the xf86Wacom.c
+file into the wacom_drv.o driver from outside the X build tree. If you
+have any comments, questions, or suggestions regarding this process,
+please send email to jej@j-arkadia.com
+<mailto:jej@j-arkadia.com?SUBJECT=WACOM_DRV> with a subject of "WACOM_DRV".
+
+
+ 9.2 - Building wacom_drv.o On Debian Stable
+
+The following documentation for building wacom_drv.o on Debian Stable
+was written by Timothy Klein. If you have any problems or questions, go
+ahead and post them to the list, or send email to me directly. If I
+cannot provide a satisfactory answer, I'll forward your email on to
+Timothy.
+
+
+
+Just thought I would give you some brief notes on compiling wacom_drv.o
+on Debian Stable. This must be done, as the Stable version of Debian
+has XFree86 v. 4.1.0.1, and your pre-compiled module is for a later
+version of X (4.2?). X complains about the server having ABI 2, and the
+module having ABI 3, and refuses to load. So one must compile the
+wacom_drv.o module for Debian Stable.
+
+The good news is that, while it requires a *huge* download, and takes a
+bit of time, it is easy.
+
+-------------------------------------------------------------
+Debian wacom_drv.o Compilation
+
+*1. Download the Debian source package for XFree86.*
+
+For Debian Stable this is three files:
+
+xfree86_4.1.0-16.diff.gz
+xfree86_4.1.0-16.dsc
+xfree86_4.1.0.orig.tar.gz
+
+Note that these are *big* files. The 'diff' file is 1.5 megabytes, and
+the 'orig' file is 52 megabytes. So if you have a 28.8 modem
+connection, this is going to be painful.
+
+These files can be obtained automatically by using the command:
+
+apt-get source xfree86
+
+It is not necessary to be root to download the source package. After it
+is downloaded, the package will be automatically extracted. Using this
+method requires that you have a valid 'source' line in your
+'/etc/apt/sources.list' file, though.
+
+You can also just download the three packages above from your favorite
+Debian mirror. If you do that, you need to extract them yourself, which
+is done with the following command:
+
+dpkg-source -x xfree86_4.1.0-16.dsc <only for manual download>
+
+Either method will give you a 'xfree86-4.1.0' directory in the download
+directory. For both this step, and the later package creation, you are
+going to need tools from the 'dpkg-dev' package. So install that with
+'apt-get' if you don't already have it installed.
+
+
+
+*2. Make sure you have the requisite build dependencies.*
+
+XFree86 requires certain packages to compile. It seems that 'dpkg' is
+pretty good about telling you what packages it needs if they are not
+installed (probably a nice script included by the Debian X maintainer).
+Thus, you can just try and compile it and see what it complains about
+not having and, at that point, install the needed packages with apt-get.
+The other, cleaner, option is to run:
+
+apt-get build-dep xfree86
+
+This command will attempt to automatically install (or once in a blue
+moon uninstall) all the necessary packages to compile XFree86. You need
+to be 'root' to do this.
+
+
+*3. Begin the Build Process for xfree86*
+
+
+Become 'root' (or you could use fakeroot), and change into the
+'xfree86-4.1.0' directory. Then run:
+
+dpkg-buildpackage
+
+The source will begin to compile. It will take a while. If you have a
+really old machine, like a Pentium 166 or such, go on a vacation. It
+may be done when you return.
+
+
+*4. Recompile the wacom_drv.o module.*
+
+The source you need to replace and recompile will be in the following
+directory:
+
+<download_dir>/xfree86-4.1.0/build-tree/xc/programs/Xserver/hw/xfree86/input/wacom
+
+Replace the 'xf86Wacom.c' file therein with the one provided. While X
+does need to have some configuration done for this to compile, and that
+configuration is accomplished automatically by the build process, it
+does not necessarily need to finish compiling the whole package. So,
+once a 'Makefile' is created automatically by the build scripts in this
+directory, you can attempt to run 'make' from time to time. Once it
+compiles without error, you can halt the very lengthy build process of
+the XFree86 package.
+
+ This may not work with the current beta package since it also
+ contains the wcm-beta.c and wcm-beta.h files. You can solve this by
+ updating the Makefile, or proceed to step five on the Building
+ wacom_drv.o From Scratch <#builddrv> page. The command line options
+ for building the driver will undoubtably have changed, so be
+ prepared to update the Makefile in the wacpack/src directory.
+
+*5. Move the wacom_drv.o module to the proper place.*
+
+That's it. Once the 'make' command compiles without error, you will
+have several new files in the directory. The one we are interested in
+is 'wacom_drv.o.' Now you can just move the 'wacom_drv.o' file to the
+directory:
+
+/usr/X11R6/lib/modules/input/
+
+This will also require 'root' privileges.
+---------------------------------------------------------------------
+
+You may want to add that to you HOWTO, for my fellow Debian users. And
+thanks again for the very nice work on the HOWTO and the drivers.
+
+HTH,
+
+Tim
+
+*Copyright (C) 2002 - John E. Joganic*
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..9c902b1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,105 @@
+noinst_PROGRAMS = @WAC_PROGS@ @WAC_XF86PROGS@
+noinst_SCRIPTS = @WAC_MODULES@ @WAC_XF86MODULES@
+
+AM_CFLAGS = -Wall
+
+EXTRA_PROGRAMS = wacdump
+EXTRA_SCRIPTS = wacom.o hid.o usbmouse.o wacom_drv.o
+
+wacdump_SOURCES = wacdump.c wacscrn.c wacscrn.h wacserial.c wacserial.h
+wacdump_LDADD = -lncurses
+EXTRA_DIST = wacom.c hid-core.c hid-input.c hiddev.c xf86Wacom.c usbmouse.c \
+ wcm-beta.c wcm-beta.h
+
+KERNEL_DIR=@WAC_KERNELDIR@
+XF86_DIR=@WAC_XF86DIR@/xc
+DEBUG_FLAGS = -D__JEJ_DEBUG
+MODS = @WAC_MODVER@
+ARCHITECTURE=@WAC_ARCH@
+USBDIR=$(KERNEL_DIR)/drivers/usb
+KCFLAGS = -Wall $(DEBUG_FLAGS) -D__KERNEL__ \
+ -DMODULE -DEXPORT_SYMTAB $(MODS) \
+ -Wstrict-prototypes -Wno-trigraphs -O2 \
+ -fno-strict-aliasing \
+ -fno-common -fomit-frame-pointer -pipe \
+ -mpreferred-stack-boundary=2 \
+ -march=$(ARCHITECTURE)
+
+wacom.o: wacom.c Makefile
+ gcc -I$(KERNEL_DIR)/include $(KCFLAGS) \
+ -DKBUILD_BASENAME=wacom -c -o wacom.o wacom.c
+
+HID_OBJS = hid-core.o hiddev.o hid-input.o
+
+hid.o: $(HID_OBJS) Makefile
+ $(LD) -r -o $@ $(HID_OBJS)
+
+hid-core.o: hid-core.c Makefile
+ gcc -I$(KERNEL_DIR)/include -I$(USBDIR) $(KCFLAGS) \
+ -DKBUILD_BASENAME=hid-core -c -o hid-core.o hid-core.c
+
+hiddev.o: $(USBDIR)/hiddev.c Makefile
+ gcc -I$(KERNEL_DIR)/include -I$(USBDIR) $(KCFLAGS) \
+ -DKBUILD_BASENAME=hiddev -c -o hiddev.o hiddev.c
+
+hid-input.o: $(USBDIR)/hid-input.c Makefile
+ gcc -I$(KERNEL_DIR)/include -I$(USBDIR) $(KCFLAGS) \
+ -DKBUILD_BASENAME=hid-input -c \
+ -o hid-input.o hid-input.c
+
+usbmouse.o: usbmouse.c Makefile
+ gcc -I$(KERNEL_DIR)/include -I$(USBDIR) $(KCFLAGS) \
+ -DKBUILD_BASENAME=usbmouse -c \
+ -o usbmouse.o usbmouse.c
+
+xf86Wacom.o: xf86Wacom.c wcm-beta.h Makefile
+ gcc -O2 -march=i386 -mcpu=$(ARCHITECTURE) -pipe -ansi \
+ -pedantic -Wall -Wpointer-arith -fno-merge-constants \
+ -I. -I$(XF86_DIR)/programs/Xserver/hw/xfree86/common \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/loader \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/os-support \
+ -I$(XF86_DIR)/programs/Xserver/include \
+ -I$(XF86_DIR)/programs/Xserver/mi \
+ -I$(XF86_DIR)/exports/include/X11 \
+ -I$(XF86_DIR)/include/extensions \
+ -I$(XF86_DIR) \
+ -I$(XF86_DIR)/exports/include \
+ -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE \
+ -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE \
+ -DSHAPE -DXINPUT -DXKB -DLBX -DXAPPGROUP -DXCSECURITY \
+ -DTOGCUP -DXF86BIGFONT -DDPMSExtension -DPIXPRIV -DPANORAMIX \
+ -DRENDER -DGCCUSESGAS -DAVOID_GLYPHBLT -DPIXPRIV \
+ -DSINGLEDEPTH -DXFreeXDGA -DXvExtension -DXFree86LOADER \
+ -DXFree86Server -DXF86VIDMODE -DXvMCExtension \
+ -DSMART_SCHEDULE -DBUILDDEBUG -DXResExtension \
+ -DX_BYTE_ORDER=X_LITTLE_ENDIAN -DNDEBUG -DFUNCPROTO=15 \
+ -DNARROWPROTO -DIN_MODULE -DXFree86Module -DLINUX_INPUT \
+ -o xf86Wacom.o -c xf86Wacom.c
+
+wcm-beta.o: wcm-beta.c wcm-beta.h Makefile
+ gcc -O2 -march=i386 -mcpu=$(ARCHITECTURE) -pipe -ansi \
+ -pedantic -Wall -Wpointer-arith -fno-merge-constants \
+ -I. -I$(XF86_DIR)/programs/Xserver/hw/xfree86/common \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/loader \
+ -I$(XF86_DIR)/programs/Xserver/hw/xfree86/os-support \
+ -I$(XF86_DIR)/programs/Xserver/include \
+ -I$(XF86_DIR)/programs/Xserver/mi \
+ -I$(XF86_DIR)/exports/include/X11 \
+ -I$(XF86_DIR)/include/extensions \
+ -I$(XF86_DIR) \
+ -I$(XF86_DIR)/exports/include \
+ -Dlinux -D__i386__ -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE \
+ -D_XOPEN_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_GNU_SOURCE \
+ -DSHAPE -DXINPUT -DXKB -DLBX -DXAPPGROUP -DXCSECURITY \
+ -DTOGCUP -DXF86BIGFONT -DDPMSExtension -DPIXPRIV -DPANORAMIX \
+ -DRENDER -DGCCUSESGAS -DAVOID_GLYPHBLT -DPIXPRIV \
+ -DSINGLEDEPTH -DXFreeXDGA -DXvExtension -DXFree86LOADER \
+ -DXFree86Server -DXF86VIDMODE -DXvMCExtension \
+ -DSMART_SCHEDULE -DBUILDDEBUG -DXResExtension \
+ -DX_BYTE_ORDER=X_LITTLE_ENDIAN -DNDEBUG -DFUNCPROTO=15 \
+ -DNARROWPROTO -DIN_MODULE -DXFree86Module -DLINUX_INPUT \
+ -o wcm-beta.o -c wcm-beta.c
+
+wacom_drv.o: xf86Wacom.o wcm-beta.o
+ ld -r xf86Wacom.o wcm-beta.o -o wacom_drv.o
+
diff --git a/src/hid-core.c b/src/hid-core.c
new file mode 100644
index 0000000..fbe2ae0
--- /dev/null
+++ b/src/hid-core.c
@@ -0,0 +1,1386 @@
+/*
+ * $Id: hid-core.c,v 1.1 2002/12/23 02:44:43 jjoganic Exp $
+ *
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * USB HID support for Linux
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include "../config.h"
+
+/*
+ * WARNING: THIS IS NOT PART OF THE OFFICIAL KERNEL TREE
+ * THIS IS FOR TESTING PURPOSES
+ *
+ * v1.8.1-j0 - merged changes from 2.4.20 with conditional 2.4.19 code
+ */
+
+#include <linux/autoconf.h>
+#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+# define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/unaligned.h>
+#include <linux/input.h>
+
+#undef DEBUG
+#undef DEBUG_DATA
+
+#include <linux/usb.h>
+
+#include "hid.h"
+#include <linux/hiddev.h>
+
+/*
+ * Version Information
+ */
+
+#define DRIVER_VERSION "v1.8.1-j0"
+#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik <vojtech@suse.cz>"
+#define DRIVER_DESC "USB HID support drivers"
+
+static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
+ "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
+
+/*
+ * Register a new report for a device.
+ */
+
+static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
+{
+ struct hid_report_enum *report_enum = device->report_enum + type;
+ struct hid_report *report;
+
+ if (report_enum->report_id_hash[id])
+ return report_enum->report_id_hash[id];
+
+ if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL)))
+ return NULL;
+ memset(report, 0, sizeof(struct hid_report));
+
+ if (id != 0) report_enum->numbered = 1;
+
+ report->id = id;
+ report->type = type;
+ report->size = 0;
+ report->device = device;
+ report_enum->report_id_hash[id] = report;
+
+ list_add_tail(&report->list, &report_enum->report_list);
+
+ return report;
+}
+
+/*
+ * Register a new field for this report.
+ */
+
+static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
+{
+ struct hid_field *field;
+
+ if (report->maxfield == HID_MAX_FIELDS) {
+ dbg("too many fields in report");
+ return NULL;
+ }
+
+ if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ + values * sizeof(unsigned), GFP_KERNEL))) return NULL;
+
+ memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ + values * sizeof(unsigned));
+
+ report->field[report->maxfield++] = field;
+ field->usage = (struct hid_usage *)(field + 1);
+ field->value = (unsigned *)(field->usage + usages);
+ field->report = report;
+
+ return field;
+}
+
+/*
+ * Open a collection. The type/usage is pushed on the stack.
+ */
+
+static int open_collection(struct hid_parser *parser, unsigned type)
+{
+ struct hid_collection *collection;
+ unsigned usage;
+
+ usage = parser->local.usage[0];
+
+ if (type == HID_COLLECTION_APPLICATION
+ && parser->device->maxapplication < HID_MAX_APPLICATIONS)
+ parser->device->application[parser->device->maxapplication++] = usage;
+
+ if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
+ dbg("collection stack overflow");
+ return -1;
+ }
+
+ collection = parser->collection_stack + parser->collection_stack_ptr++;
+ collection->type = type;
+ collection->usage = usage;
+
+ return 0;
+}
+
+/*
+ * Close a collection.
+ */
+
+static int close_collection(struct hid_parser *parser)
+{
+ if (!parser->collection_stack_ptr) {
+ dbg("collection stack underflow");
+ return -1;
+ }
+ parser->collection_stack_ptr--;
+ return 0;
+}
+
+/*
+ * Climb up the stack, search for the specified collection type
+ * and return the usage.
+ */
+
+static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
+{
+ int n;
+ for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
+ if (parser->collection_stack[n].type == type)
+ return parser->collection_stack[n].usage;
+ return 0; /* we know nothing about this usage type */
+}
+
+/*
+ * Add a usage to the temporary parser table.
+ */
+
+static int hid_add_usage(struct hid_parser *parser, unsigned usage)
+{
+ if (parser->local.usage_index >= HID_MAX_USAGES) {
+ dbg("usage index exceeded");
+ return -1;
+ }
+ parser->local.usage[parser->local.usage_index++] = usage;
+ return 0;
+}
+
+/*
+ * Register a new field for this report.
+ */
+
+static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags)
+{
+ struct hid_report *report;
+ struct hid_field *field;
+ int usages;
+ unsigned offset;
+ int i;
+
+ if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
+ dbg("hid_register_report failed");
+ return -1;
+ }
+
+ if (parser->global.logical_maximum <= parser->global.logical_minimum) {
+ dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
+ return -1;
+ }
+
+ usages = parser->local.usage_index;
+
+ offset = report->size;
+ report->size += parser->global.report_size * parser->global.report_count;
+
+ if (usages == 0)
+ return 0; /* ignore padding fields */
+
+ if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
+ return 0;
+
+ field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
+ field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
+ field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
+
+ for (i = 0; i < usages; i++)
+ field->usage[i].hid = parser->local.usage[i];
+
+ field->maxusage = usages;
+ field->flags = flags;
+ field->report_offset = offset;
+ field->report_type = report_type;
+ field->report_size = parser->global.report_size;
+ field->report_count = parser->global.report_count;
+ field->logical_minimum = parser->global.logical_minimum;
+ field->logical_maximum = parser->global.logical_maximum;
+ field->physical_minimum = parser->global.physical_minimum;
+ field->physical_maximum = parser->global.physical_maximum;
+ field->unit_exponent = parser->global.unit_exponent;
+ field->unit = parser->global.unit;
+
+ return 0;
+}
+
+/*
+ * Read data value from item.
+ */
+
+static __inline__ __u32 item_udata(struct hid_item *item)
+{
+ switch (item->size) {
+ case 1: return item->data.u8;
+ case 2: return item->data.u16;
+ case 4: return item->data.u32;
+ }
+ return 0;
+}
+
+static __inline__ __s32 item_sdata(struct hid_item *item)
+{
+ switch (item->size) {
+ case 1: return item->data.s8;
+ case 2: return item->data.s16;
+ case 4: return item->data.s32;
+ }
+ return 0;
+}
+
+/*
+ * Process a global item.
+ */
+
+static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
+{
+ switch (item->tag) {
+
+ case HID_GLOBAL_ITEM_TAG_PUSH:
+
+ if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
+ dbg("global enviroment stack overflow");
+ return -1;
+ }
+
+ memcpy(parser->global_stack + parser->global_stack_ptr++,
+ &parser->global, sizeof(struct hid_global));
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_POP:
+
+ if (!parser->global_stack_ptr) {
+ dbg("global enviroment stack underflow");
+ return -1;
+ }
+
+ memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
+ sizeof(struct hid_global));
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+ parser->global.usage_page = item_udata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+ parser->global.logical_minimum = item_sdata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+ if (parser->global.logical_minimum < 0)
+ parser->global.logical_maximum = item_sdata(item);
+ else
+ parser->global.logical_maximum = item_udata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+ parser->global.physical_minimum = item_sdata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+ if (parser->global.physical_minimum < 0)
+ parser->global.physical_maximum = item_sdata(item);
+ else
+ parser->global.physical_maximum = item_udata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+ parser->global.unit_exponent = item_udata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_UNIT:
+ parser->global.unit = item_udata(item);
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+ if ((parser->global.report_size = item_udata(item)) > 32) {
+ dbg("invalid report_size %d", parser->global.report_size);
+ return -1;
+ }
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+ if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
+ dbg("invalid report_count %d", parser->global.report_count);
+ return -1;
+ }
+ return 0;
+
+ case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+ if ((parser->global.report_id = item_udata(item)) == 0) {
+ dbg("report_id 0 is invalid");
+ return -1;
+ }
+ return 0;
+
+ default:
+ dbg("unknown global tag 0x%x", item->tag);
+ return -1;
+ }
+}
+
+/*
+ * Process a local item.
+ */
+
+static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
+{
+ __u32 data;
+ unsigned n;
+
+ if (item->size == 0) {
+ dbg("item data expected for local item");
+ return -1;
+ }
+
+ data = item_udata(item);
+
+ switch (item->tag) {
+
+ case HID_LOCAL_ITEM_TAG_DELIMITER:
+
+ if (data) {
+ /*
+ * We treat items before the first delimiter
+ * as global to all usage sets (branch 0).
+ * In the moment we process only these global
+ * items and the first delimiter set.
+ */
+ if (parser->local.delimiter_depth != 0) {
+ dbg("nested delimiters");
+ return -1;
+ }
+ parser->local.delimiter_depth++;
+ parser->local.delimiter_branch++;
+ } else {
+ if (parser->local.delimiter_depth < 1) {
+ dbg("bogus close delimiter");
+ return -1;
+ }
+ parser->local.delimiter_depth--;
+ }
+ return 1;
+
+ case HID_LOCAL_ITEM_TAG_USAGE:
+
+ if (parser->local.delimiter_branch > 1) {
+ dbg("alternative usage ignored");
+ return 0;
+ }
+
+ if (item->size <= 2)
+ data = (parser->global.usage_page << 16) + data;
+
+ return hid_add_usage(parser, data);
+
+ case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+
+ if (parser->local.delimiter_branch > 1) {
+ dbg("alternative usage ignored");
+ return 0;
+ }
+
+ if (item->size <= 2)
+ data = (parser->global.usage_page << 16) + data;
+
+ parser->local.usage_minimum = data;
+ return 0;
+
+ case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+
+ if (parser->local.delimiter_branch > 1) {
+ dbg("alternative usage ignored");
+ return 0;
+ }
+
+ if (item->size <= 2)
+ data = (parser->global.usage_page << 16) + data;
+
+ for (n = parser->local.usage_minimum; n <= data; n++)
+ if (hid_add_usage(parser, n)) {
+ dbg("hid_add_usage failed\n");
+ return -1;
+ }
+ return 0;
+
+ default:
+
+ dbg("unknown local item tag 0x%x", item->tag);
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * Process a main item.
+ */
+
+static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
+{
+ __u32 data;
+ int ret;
+
+ data = item_udata(item);
+
+ switch (item->tag) {
+ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+ ret = open_collection(parser, data & 3);
+ break;
+ case HID_MAIN_ITEM_TAG_END_COLLECTION:
+ ret = close_collection(parser);
+ break;
+ case HID_MAIN_ITEM_TAG_INPUT:
+ ret = hid_add_field(parser, HID_INPUT_REPORT, data);
+ break;
+ case HID_MAIN_ITEM_TAG_OUTPUT:
+ ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
+ break;
+ case HID_MAIN_ITEM_TAG_FEATURE:
+ ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
+ break;
+ default:
+ dbg("unknown main item tag 0x%x", item->tag);
+ ret = 0;
+ }
+
+ memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */
+
+ return ret;
+}
+
+/*
+ * Process a reserved item.
+ */
+
+static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item)
+{
+ dbg("reserved item type, tag 0x%x", item->tag);
+ return 0;
+}
+
+/*
+ * Free a report and all registered fields. The field->usage and
+ * field->value table's are allocated behind the field, so we need
+ * only to free(field) itself.
+ */
+
+static void hid_free_report(struct hid_report *report)
+{
+ unsigned n;
+
+ for (n = 0; n < report->maxfield; n++)
+ kfree(report->field[n]);
+ if (report->data)
+ kfree(report->data);
+ kfree(report);
+}
+
+/*
+ * Free a device structure, all reports, and all fields.
+ */
+
+static void hid_free_device(struct hid_device *device)
+{
+ unsigned i,j;
+
+ for (i = 0; i < HID_REPORT_TYPES; i++) {
+ struct hid_report_enum *report_enum = device->report_enum + i;
+
+ for (j = 0; j < 256; j++) {
+ struct hid_report *report = report_enum->report_id_hash[j];
+ if (report) hid_free_report(report);
+ }
+ }
+
+ if (device->rdesc) kfree(device->rdesc);
+}
+
+/*
+ * Fetch a report description item from the data stream. We support long
+ * items, though they are not used yet.
+ */
+
+static __u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
+{
+ if ((end - start) > 0) {
+
+ __u8 b = *start++;
+ item->type = (b >> 2) & 3;
+ item->tag = (b >> 4) & 15;
+
+ if (item->tag == HID_ITEM_TAG_LONG) {
+
+ item->format = HID_ITEM_FORMAT_LONG;
+
+ if ((end - start) >= 2) {
+
+ item->size = *start++;
+ item->tag = *start++;
+
+ if ((end - start) >= item->size) {
+ item->data.longdata = start;
+ start += item->size;
+ return start;
+ }
+ }
+ } else {
+
+ item->format = HID_ITEM_FORMAT_SHORT;
+ item->size = b & 3;
+ switch (item->size) {
+
+ case 0:
+ return start;
+
+ case 1:
+ if ((end - start) >= 1) {
+ item->data.u8 = *start++;
+ return start;
+ }
+ break;
+
+ case 2:
+ if ((end - start) >= 2) {
+ item->data.u16 = le16_to_cpu( get_unaligned(((__u16*)start)++));
+ return start;
+ }
+
+ case 3:
+ item->size++;
+ if ((end - start) >= 4) {
+ item->data.u32 = le32_to_cpu( get_unaligned(((__u32*)start)++));
+ return start;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Parse a report description into a hid_device structure. Reports are
+ * enumerated, fields are attached to these reports.
+ */
+
+static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
+{
+ struct hid_device *device;
+ struct hid_parser *parser;
+ struct hid_item item;
+ __u8 *end;
+ unsigned i;
+ static int (*dispatch_type[])(struct hid_parser *parser,
+ struct hid_item *item) = {
+ hid_parser_main,
+ hid_parser_global,
+ hid_parser_local,
+ hid_parser_reserved
+ };
+
+ if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL)))
+ return NULL;
+ memset(device, 0, sizeof(struct hid_device));
+
+ for (i = 0; i < HID_REPORT_TYPES; i++)
+ INIT_LIST_HEAD(&device->report_enum[i].report_list);
+
+ if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
+ kfree(device);
+ return NULL;
+ }
+ memcpy(device->rdesc, start, size);
+
+ if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
+ kfree(device->rdesc);
+ kfree(device);
+ return NULL;
+ }
+ memset(parser, 0, sizeof(struct hid_parser));
+ parser->device = device;
+
+ end = start + size;
+ while ((start = fetch_item(start, end, &item)) != 0) {
+ if (item.format != HID_ITEM_FORMAT_SHORT) {
+ dbg("unexpected long global item");
+ hid_free_device(device);
+ kfree(parser);
+ return NULL;
+ }
+ if (dispatch_type[item.type](parser, &item)) {
+ dbg("item %u %u %u %u parsing failed\n",
+ item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
+ hid_free_device(device);
+ kfree(parser);
+ return NULL;
+ }
+
+ if (start == end) {
+ if (parser->collection_stack_ptr) {
+ dbg("unbalanced collection at end of report description");
+ hid_free_device(device);
+ kfree(parser);
+ return NULL;
+ }
+ if (parser->local.delimiter_depth) {
+ dbg("unbalanced delimiter at end of report description");
+ hid_free_device(device);
+ kfree(parser);
+ return NULL;
+ }
+ kfree(parser);
+ return device;
+ }
+ }
+
+ dbg("item fetching failed at offset %d\n", (int)(end - start));
+ hid_free_device(device);
+ kfree(parser);
+ return NULL;
+}
+
+/*
+ * Convert a signed n-bit integer to signed 32-bit integer. Common
+ * cases are done through the compiler, the screwed things has to be
+ * done by hand.
+ */
+
+static __inline__ __s32 snto32(__u32 value, unsigned n)
+{
+ switch (n) {
+ case 8: return ((__s8)value);
+ case 16: return ((__s16)value);
+ case 32: return ((__s32)value);
+ }
+ return value & (1 << (n - 1)) ? value | (-1 << n) : value;
+}
+
+/*
+ * Convert a signed 32-bit integer to a signed n-bit integer.
+ */
+
+static __inline__ __u32 s32ton(__s32 value, unsigned n)
+{
+ __s32 a = value >> (n - 1);
+ if (a && a != -1) return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1;
+ return value & ((1 << n) - 1);
+}
+
+/*
+ * Extract/implement a data field from/to a report.
+ */
+
+static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
+{
+ report += (offset >> 5) << 2; offset &= 31;
+ return (le64_to_cpu(get_unaligned((__u64*)report)) >> offset) & ((1 << n) - 1);
+}
+
+static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
+{
+ report += (offset >> 5) << 2; offset &= 31;
+ put_unaligned((get_unaligned((__u64*)report)
+ & cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)))
+ | cpu_to_le64((__u64)value << offset), (__u64*)report);
+}
+
+/*
+ * Search an array for a value.
+ */
+
+static __inline__ int search(__s32 *array, __s32 value, unsigned n)
+{
+ while (n--) if (*array++ == value) return 0;
+ return -1;
+}
+
+static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
+{
+ hid_dump_input(usage, value);
+ if (hid->claimed & HID_CLAIMED_INPUT)
+ hidinput_hid_event(hid, field, usage, value);
+ if (hid->claimed & HID_CLAIMED_HIDDEV)
+ hiddev_hid_event(hid, usage->hid, value);
+}
+
+
+/*
+ * Analyse a received field, and fetch the data from it. The field
+ * content is stored for next report processing (we do differential
+ * reporting to the layer).
+ */
+
+static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data)
+{
+ unsigned n;
+ unsigned count = field->report_count;
+ unsigned offset = field->report_offset;
+ unsigned size = field->report_size;
+ __s32 min = field->logical_minimum;
+ __s32 max = field->logical_maximum;
+ __s32 value[count]; /* WARNING: gcc specific */
+
+ for (n = 0; n < count; n++) {
+
+ value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) :
+ extract(data, offset + n * size, size);
+
+ if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */
+ && value[n] >= min && value[n] <= max
+ && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
+ return;
+ }
+
+ for (n = 0; n < count; n++) {
+
+ if (HID_MAIN_ITEM_VARIABLE & field->flags) {
+
+ if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+ if (!value[n]) continue;
+ } else {
+ if (value[n] == field->value[n]) continue;
+ }
+ hid_process_event(hid, field, &field->usage[n], value[n]);
+ continue;
+ }
+
+ if (field->value[n] >= min && field->value[n] <= max
+ && field->usage[field->value[n] - min].hid
+ && search(value, field->value[n], count))
+ hid_process_event(hid, field, &field->usage[field->value[n] - min], 0);
+
+ if (value[n] >= min && value[n] <= max
+ && field->usage[value[n] - min].hid
+ && search(field->value, value[n], count))
+ hid_process_event(hid, field, &field->usage[value[n] - min], 1);
+ }
+
+ memcpy(field->value, value, count * sizeof(__s32));
+}
+
+static int hid_input_report(int type, u8 *data, int len, struct hid_device *hid)
+{
+ struct hid_report_enum *report_enum = hid->report_enum + type;
+ struct hid_report *report;
+ int n, size;
+
+ if (!len) {
+ dbg("empty report");
+ return -1;
+ }
+
+#ifdef DEBUG_DATA
+ printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", len, report_enum->numbered ? "" : "un");
+#endif
+
+ n = 0; /* Normally report number is 0 */
+ if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */
+ n = *data++;
+ len--;
+ }
+
+ if (!(report = report_enum->report_id_hash[n])) {
+ dbg("undefined report_id %d received", n);
+#ifdef DEBUG
+ printk(KERN_DEBUG __FILE__ ": report (size %u) = ", len);
+ for (n = 0; n < len; n++)
+ printk(" %02x", data[n]);
+ printk("\n");
+#endif
+
+ return -1;
+ }
+
+ size = ((report->size - 1) >> 3) + 1;
+
+ if (len < size) {
+
+ if (size <= 8) {
+ dbg("report %d is too short, (%d < %d)", report->id, len, size);
+ return -1;
+ }
+
+ /*
+ * Some low-speed devices have large reports and maxpacketsize 8.
+ * We buffer the data in that case and parse it when we got it all.
+ * Works only for unnumbered reports. Doesn't make sense for numbered
+ * reports anyway - then they don't need to be large.
+ */
+
+ if (!report->data)
+ if (!(report->data = kmalloc(size, GFP_ATOMIC))) {
+ dbg("couldn't allocate report buffer");
+ return -1;
+ }
+
+ if (report->idx + len > size) {
+ dbg("report data buffer overflow");
+ report->idx = 0;
+ return -1;
+ }
+
+ memcpy(report->data + report->idx, data, len);
+ report->idx += len;
+
+ if (report->idx < size)
+ return 0;
+
+ data = report->data;
+ }
+
+ for (n = 0; n < report->maxfield; n++)
+ hid_input_field(hid, report->field[n], data);
+
+ report->idx = 0;
+ return 0;
+}
+
+/*
+ * Interrupt input handler.
+ */
+
+static void hid_irq(struct urb *urb)
+{
+ if (urb->status) {
+ dbg("nonzero status in irq %d", urb->status);
+ return;
+ }
+
+ hid_input_report(HID_INPUT_REPORT, urb->transfer_buffer, urb->actual_length, urb->context);
+}
+
+/*
+ * hid_read_report() reads in report values without waiting for an irq urb.
+ */
+
+void hid_read_report(struct hid_device *hid, struct hid_report *report)
+{
+ int len = ((report->size - 1) >> 3) + 1 + hid->report_enum[report->type].numbered;
+ u8 data[len];
+ int read;
+
+ if (hid->quirks & HID_QUIRK_NOGET)
+ return;
+
+ if ((read = usb_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, data, len)) != len) {
+ dbg("reading report type %d id %d failed len %d read %d", report->type + 1, report->id, len, read);
+ return;
+ }
+
+ hid_input_report(report->type, data, len, hid);
+}
+
+/*
+ * Output the field into the report.
+ */
+
+static void hid_output_field(struct hid_field *field, __u8 *data)
+{
+ unsigned count = field->report_count;
+ unsigned offset = field->report_offset;
+ unsigned size = field->report_size;
+ unsigned n;
+
+ for (n = 0; n < count; n++) {
+ if (field->logical_minimum < 0) /* signed values */
+ implement(data, offset + n * size, size, s32ton(field->value[n], size));
+ else /* unsigned values */
+ implement(data, offset + n * size, size, field->value[n]);
+ }
+}
+
+/*
+ * Create a report.
+ */
+
+void hid_output_report(struct hid_report *report, __u8 *data)
+{
+ unsigned n;
+ for (n = 0; n < report->maxfield; n++)
+ hid_output_field(report->field[n], data);
+}
+
+/*
+ * Set a field value. The report this field belongs to has to be
+ * created and transfered to the device, to set this value in the
+ * device.
+ */
+
+int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
+{
+ unsigned size = field->report_size;
+
+ hid_dump_input(field->usage + offset, value);
+
+ if (offset >= field->report_count) {
+ dbg("offset exceeds report_count");
+ return -1;
+ }
+ if (field->logical_minimum < 0) {
+ if (value != snto32(s32ton(value, size), size)) {
+ dbg("value %d is out of range", value);
+ return -1;
+ }
+ }
+ if ( (value > field->logical_maximum)
+ || (value < field->logical_minimum)) {
+ dbg("value %d is invalid", value);
+ return -1;
+ }
+ field->value[offset] = value;
+ return 0;
+}
+
+int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
+{
+ struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT;
+ struct list_head *list = report_enum->report_list.next;
+ int i, j;
+
+ while (list != &report_enum->report_list) {
+ struct hid_report *report = (struct hid_report *) list;
+ list = list->next;
+ for (i = 0; i < report->maxfield; i++) {
+ *field = report->field[i];
+ for (j = 0; j < (*field)->maxusage; j++)
+ if ((*field)->usage[j].type == type && (*field)->usage[j].code == code)
+ return j;
+ }
+ }
+ return -1;
+}
+
+static int hid_submit_out(struct hid_device *hid)
+{
+#if WAC_PATCH_DRVALUE
+ hid->urbout.transfer_buffer_length = le16_to_cpup(&hid->out[hid->outtail].dr.wLength);
+#else
+ hid->urbout.transfer_buffer_length = le16_to_cpup(&hid->out[hid->outtail].dr.length);
+#endif
+
+ hid->urbout.transfer_buffer = hid->out[hid->outtail].buffer;
+ hid->urbout.setup_packet = (void *) &(hid->out[hid->outtail].dr);
+ hid->urbout.dev = hid->dev;
+
+ if (usb_submit_urb(&hid->urbout)) {
+ err("usb_submit_urb(out) failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void hid_ctrl(struct urb *urb)
+{
+ struct hid_device *hid = urb->context;
+
+ if (urb->status)
+ warn("ctrl urb status %d received", urb->status);
+
+ hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+ if (hid->outhead != hid->outtail)
+ hid_submit_out(hid);
+}
+
+void hid_write_report(struct hid_device *hid, struct hid_report *report)
+{
+ hid_output_report(report, hid->out[hid->outhead].buffer);
+
+#if WAC_PATCH_DRVALUE
+ hid->out[hid->outhead].dr.wValue = cpu_to_le16(0x200 | report->id);
+ hid->out[hid->outhead].dr.wLength = cpu_to_le16((report->size + 7) >> 3);
+#else
+ hid->out[hid->outhead].dr.value = cpu_to_le16(0x200 | report->id);
+ hid->out[hid->outhead].dr.length = cpu_to_le16((report->size + 7) >> 3);
+#endif
+
+ hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+ if (hid->outhead == hid->outtail)
+ hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+ if (hid->urbout.status != -EINPROGRESS)
+ hid_submit_out(hid);
+}
+
+int hid_open(struct hid_device *hid)
+{
+ if (hid->open++)
+ return 0;
+
+ hid->urb.dev = hid->dev;
+
+ if (usb_submit_urb(&hid->urb))
+ return -EIO;
+
+ return 0;
+}
+
+void hid_close(struct hid_device *hid)
+{
+ if (!--hid->open)
+ usb_unlink_urb(&hid->urb);
+}
+
+/*
+ * Initialize all readable reports
+ */
+void hid_init_reports(struct hid_device *hid)
+{
+ int i;
+ struct hid_report *report;
+ struct hid_report_enum *report_enum;
+ struct list_head *list;
+
+ for (i = 0; i < HID_REPORT_TYPES; i++) {
+ if (i == HID_FEATURE_REPORT || i == HID_INPUT_REPORT) {
+ report_enum = hid->report_enum + i;
+ list = report_enum->report_list.next;
+ while (list != &report_enum->report_list) {
+ report = (struct hid_report *) list;
+ /* JEJ: 2.4.20 patch */
+ usb_set_idle(hid->dev, hid->ifnum, 0, report->id);
+ hid_read_report(hid, report);
+ list = list->next;
+ }
+ }
+ }
+}
+
+#define USB_VENDOR_ID_WACOM 0x056a
+#define USB_DEVICE_ID_WACOM_PENPARTNER 0x0000
+#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
+#define USB_DEVICE_ID_WACOM_INTUOS 0x0020
+#define USB_DEVICE_ID_WACOM_PL 0x0030
+#define USB_DEVICE_ID_WACOM_INTUOS2 0x0041
+
+#define USB_VENDOR_ID_ATEN 0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
+#define USB_DEVICE_ID_ATEN_CS124U 0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
+
+struct hid_blacklist {
+ __u16 idVendor;
+ __u16 idProduct;
+ unsigned quirks;
+} hid_blacklist[] = {
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PENPARTNER, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 4, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 5, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 4, HID_QUIRK_IGNORE },
+
+ /* JEJ - exists in wacom.c, so it should exist here, too */
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 5, HID_QUIRK_IGNORE },
+
+ /* JEJ - added as test for M. T. (Intuos2 6x8 model XD-0608-U) */
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
+ { 0, 0 }
+};
+
+static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
+{
+ struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0;
+ struct hid_descriptor *hdesc;
+ struct hid_device *hid;
+ unsigned quirks = 0, rsize = 0;
+ char *buf;
+ int n;
+
+ for (n = 0; hid_blacklist[n].idVendor; n++)
+ if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) &&
+ (hid_blacklist[n].idProduct == dev->descriptor.idProduct))
+ quirks = hid_blacklist[n].quirks;
+
+#if __DEBUG_JEJ
+ printk(KERN_INFO __FILE__ ": usb_hid_configure: "
+ "configuring device %X.%X%s, quirk is %X%s\n",
+ hid_blacklist[n].idVendor,
+ hid_blacklist[n].idProduct,
+ (hidblacklist[n].idVendor == USB_VENDOR_ID_WACOM) ? " (WACOM)" : "",
+ quirks, (quirks & HID_QUIRK_IGNORE) ? " (IGNORE)" : "");
+#endif /* __DEBUG_JEJ */
+
+ if (quirks & HID_QUIRK_IGNORE)
+ return NULL;
+
+ if (usb_get_extra_descriptor(interface, USB_DT_HID, &hdesc) && ((!interface->bNumEndpoints) ||
+ usb_get_extra_descriptor(&interface->endpoint[0], USB_DT_HID, &hdesc))) {
+ dbg("class descriptor not present\n");
+ return NULL;
+ }
+
+ for (n = 0; n < hdesc->bNumDescriptors; n++)
+ if (hdesc->desc[n].bDescriptorType == USB_DT_REPORT)
+ rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
+
+ if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+ dbg("weird size of report descriptor (%u)", rsize);
+ return NULL;
+ }
+
+ {
+ __u8 rdesc[rsize];
+
+ if ((n = usb_get_class_descriptor(dev, interface->bInterfaceNumber, USB_DT_REPORT, 0, rdesc, rsize)) < 0) {
+ dbg("reading report descriptor failed");
+ return NULL;
+ }
+
+#ifdef DEBUG_DATA
+ printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
+ for (n = 0; n < rsize; n++)
+ printk(" %02x", (unsigned) rdesc[n]);
+ printk("\n");
+#endif
+
+ if (!(hid = hid_parse_report(rdesc, rsize))) {
+ dbg("parsing report descriptor failed");
+ return NULL;
+ }
+ }
+
+ hid->quirks = quirks;
+
+ for (n = 0; n < interface->bNumEndpoints; n++) {
+
+ struct usb_endpoint_descriptor *endpoint = &interface->endpoint[n];
+ int pipe, maxp;
+
+ if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
+ continue;
+
+ if (!(endpoint->bEndpointAddress & 0x80)) /* Not an input endpoint */
+ continue;
+
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval);
+
+ break;
+ }
+
+ if (n == interface->bNumEndpoints) {
+ dbg("couldn't find an input interrupt endpoint");
+ hid_free_device(hid);
+ return NULL;
+ }
+
+ hid->version = hdesc->bcdHID;
+ hid->country = hdesc->bCountryCode;
+ hid->dev = dev;
+ hid->ifnum = interface->bInterfaceNumber;
+
+ for (n = 0; n < HID_CONTROL_FIFO_SIZE; n++) {
+#if WAC_PATCH_DRVALUE
+ hid->out[n].dr.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ hid->out[n].dr.bRequest = USB_REQ_SET_REPORT;
+ hid->out[n].dr.wIndex = cpu_to_le16(hid->ifnum);
+#else
+ hid->out[n].dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ hid->out[n].dr.request = USB_REQ_SET_REPORT;
+ hid->out[n].dr.index = cpu_to_le16(hid->ifnum);
+#endif
+ }
+
+ hid->name[0] = 0;
+
+ if (!(buf = kmalloc(63, GFP_KERNEL)))
+ return NULL;
+
+ if (usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) {
+ strcat(hid->name, buf);
+ if (usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+ sprintf(hid->name, "%s %s", hid->name, buf);
+ } else
+ sprintf(hid->name, "%04x:%04x", dev->descriptor.idVendor, dev->descriptor.idProduct);
+
+ kfree(buf);
+
+ FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0),
+ (void*) &hid->out[0].dr, hid->out[0].buffer, 1, hid_ctrl, hid);
+
+/*
+ * Some devices don't like this and crash. I don't know of any devices
+ * needing this, so it is disabled for now.
+ */
+
+#if 0
+ if (interface->bInterfaceSubClass == 1)
+ usb_set_protocol(dev, hid->ifnum, 1);
+#endif
+
+ return hid;
+}
+
+static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *id)
+{
+ struct hid_device *hid;
+ int i;
+ char *c;
+
+ dbg("HID probe called for ifnum %d", ifnum);
+
+ if (!(hid = usb_hid_configure(dev, ifnum)))
+ return NULL;
+
+ hid_init_reports(hid);
+ hid_dump_device(hid);
+
+ if (!hidinput_connect(hid))
+ hid->claimed |= HID_CLAIMED_INPUT;
+ if (!hiddev_connect(hid))
+ hid->claimed |= HID_CLAIMED_HIDDEV;
+ printk(KERN_INFO);
+
+ if (hid->claimed & HID_CLAIMED_INPUT)
+ printk("input%d", hid->input.number);
+ if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
+ printk(",");
+ if (hid->claimed & HID_CLAIMED_HIDDEV)
+ printk("hiddev%d", hid->minor);
+
+ c = "Device";
+ for (i = 0; i < hid->maxapplication; i++)
+ if ((hid->application[i] & 0xffff) < ARRAY_SIZE(hid_types)) {
+ c = hid_types[hid->application[i] & 0xffff];
+ break;
+ }
+
+ printk(": USB HID v%x.%02x %s [%s] on usb%d:%d.%d\n",
+ hid->version >> 8, hid->version & 0xff, c, hid->name,
+ dev->bus->busnum, dev->devnum, ifnum);
+
+ return hid;
+}
+
+static void hid_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct hid_device *hid = ptr;
+
+ dbg("cleanup called");
+ usb_unlink_urb(&hid->urb);
+ if (hid->claimed & HID_CLAIMED_INPUT)
+ hidinput_disconnect(hid);
+ if (hid->claimed & HID_CLAIMED_HIDDEV)
+ hiddev_disconnect(hid);
+ hid_free_device(hid);
+}
+
+static struct usb_device_id hid_usb_ids [] = {
+ { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS,
+ bInterfaceClass: USB_INTERFACE_CLASS_HID },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hid_usb_ids);
+
+static struct usb_driver hid_driver = {
+ name: "hid",
+ probe: hid_probe,
+ disconnect: hid_disconnect,
+ id_table: hid_usb_ids,
+};
+
+static int __init hid_init(void)
+{
+#ifdef __JEJ_DEBUG
+ printk(KERN_INFO __FILE__ ": hid_init (MODIFIED DEBUG)\n");
+#endif
+ hiddev_init();
+ usb_register(&hid_driver);
+ info(DRIVER_VERSION " " DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+
+ return 0;
+}
+
+static void __exit hid_exit(void)
+{
+ hiddev_exit();
+ usb_deregister(&hid_driver);
+}
+
+module_init(hid_init);
+module_exit(hid_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff --git a/src/hid-input.c b/src/hid-input.c
new file mode 100644
index 0000000..bcb42f8
--- /dev/null
+++ b/src/hid-input.c
@@ -0,0 +1,434 @@
+/*
+ * $Id: hid-input.c,v 1.1 2002/12/23 02:44:44 jjoganic Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * USB HID to Linux Input mapping module
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/autoconf.h>
+#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+# define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#include "hid.h"
+
+#define unk KEY_UNKNOWN
+
+static unsigned char hid_keyboard[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
+ 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+ 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95,
+ 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113,
+ 115,114,unk,unk,unk,124,unk,181,182,183,184,185,186,187,188,189,
+ 190,191,192,193,194,195,196,197,198,unk,unk,unk,unk,unk,unk,unk,
+ unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+ unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+ unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+ unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+ 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
+};
+
+static struct {
+ __s32 x;
+ __s32 y;
+} hid_hat_to_axis[] = {{0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+static void hidinput_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage)
+{
+ struct input_dev *input = &device->input;
+ int max;
+ unsigned long *bit;
+
+ switch (usage->hid & HID_USAGE_PAGE) {
+
+ case HID_UP_KEYBOARD:
+
+ set_bit(EV_REP, input->evbit);
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+
+ if ((usage->hid & HID_USAGE) < 256) {
+ if (!(usage->code = hid_keyboard[usage->hid & HID_USAGE]))
+ return;
+ clear_bit(usage->code, bit);
+ } else
+ usage->code = KEY_UNKNOWN;
+
+ break;
+
+ case HID_UP_BUTTON:
+
+ usage->code = ((usage->hid - 1) & 0xf) + 0x100;
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+
+ switch (field->application) {
+ case HID_GD_GAMEPAD: usage->code += 0x10;
+ case HID_GD_JOYSTICK: usage->code += 0x10;
+ case HID_GD_MOUSE: usage->code += 0x10; break;
+ default:
+ if (field->physical == HID_GD_POINTER)
+ usage->code += 0x10;
+ break;
+ }
+ break;
+
+ case HID_UP_GENDESK:
+
+ if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */
+ switch (usage->hid & 0xf) {
+ case 0x1: usage->code = KEY_POWER; break;
+ case 0x2: usage->code = KEY_SLEEP; break;
+ case 0x3: usage->code = KEY_WAKEUP; break;
+ default: usage->code = KEY_UNKNOWN; break;
+ }
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ break;
+ }
+
+ usage->code = usage->hid & 0xf;
+
+ if (field->report_size == 1) {
+ usage->code = BTN_MISC;
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ break;
+ }
+
+ if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+ usage->type = EV_REL; bit = input->relbit; max = REL_MAX;
+ break;
+ }
+
+ usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
+
+ if (usage->hid == HID_GD_HATSWITCH) {
+ usage->code = ABS_HAT0X;
+ usage->hat_min = field->logical_minimum;
+ usage->hat_max = field->logical_maximum;
+ }
+ break;
+
+ case HID_UP_LED:
+
+ usage->code = (usage->hid - 1) & 0xf;
+ usage->type = EV_LED; bit = input->ledbit; max = LED_MAX;
+ break;
+
+ case HID_UP_DIGITIZER:
+
+ switch (usage->hid & 0xff) {
+
+ case 0x30: /* TipPressure */
+
+ if (!test_bit(BTN_TOUCH, input->keybit)) {
+ device->quirks |= HID_QUIRK_NOTOUCH;
+ set_bit(EV_KEY, input->evbit);
+ set_bit(BTN_TOUCH, input->keybit);
+ }
+ usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
+ usage->code = ABS_PRESSURE;
+ clear_bit(usage->code, bit);
+ break;
+
+ case 0x32: /* InRange */
+
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ switch (field->physical & 0xff) {
+ case 0x21: usage->code = BTN_TOOL_MOUSE; break;
+ case 0x22: usage->code = BTN_TOOL_FINGER; break;
+ default: usage->code = BTN_TOOL_PEN; break;
+ }
+ break;
+
+ case 0x3c: /* Invert */
+
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ usage->code = BTN_TOOL_RUBBER;
+ clear_bit(usage->code, bit);
+ break;
+
+ case 0x33: /* Touch */
+ case 0x42: /* TipSwitch */
+ case 0x43: /* TipSwitch2 */
+
+ device->quirks &= ~HID_QUIRK_NOTOUCH;
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ usage->code = BTN_TOUCH;
+ clear_bit(usage->code, bit);
+ break;
+
+ case 0x44: /* BarrelSwitch */
+
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ usage->code = BTN_STYLUS;
+ clear_bit(usage->code, bit);
+ break;
+
+ default: goto unknown;
+ }
+ break;
+
+ case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */
+
+ switch (usage->hid & HID_USAGE) {
+ case 0x000: usage->code = 0; break;
+ case 0x034: usage->code = KEY_SLEEP; break;
+ case 0x036: usage->code = BTN_MISC; break;
+ case 0x08a: usage->code = KEY_WWW; break;
+ case 0x095: usage->code = KEY_HELP; break;
+
+ case 0x0b4: usage->code = KEY_REWIND; break;
+ case 0x0b5: usage->code = KEY_NEXTSONG; break;
+ case 0x0b6: usage->code = KEY_PREVIOUSSONG; break;
+ case 0x0b7: usage->code = KEY_STOPCD; break;
+ case 0x0b8: usage->code = KEY_EJECTCD; break;
+ case 0x0cd: usage->code = KEY_PLAYPAUSE; break;
+
+ case 0x0e2: usage->code = KEY_MUTE; break;
+ case 0x0e9: usage->code = KEY_VOLUMEUP; break;
+ case 0x0ea: usage->code = KEY_VOLUMEDOWN; break;
+
+ case 0x183: usage->code = KEY_CONFIG; break;
+ case 0x18a: usage->code = KEY_MAIL; break;
+ case 0x192: usage->code = KEY_CALC; break;
+ case 0x194: usage->code = KEY_FILE; break;
+
+ case 0x21a: usage->code = KEY_UNDO; break;
+ case 0x21b: usage->code = KEY_COPY; break;
+ case 0x21c: usage->code = KEY_CUT; break;
+ case 0x21d: usage->code = KEY_PASTE; break;
+
+ case 0x221: usage->code = KEY_FIND; break;
+ case 0x223: usage->code = KEY_HOMEPAGE; break;
+ case 0x224: usage->code = KEY_BACK; break;
+ case 0x225: usage->code = KEY_FORWARD; break;
+ case 0x226: usage->code = KEY_STOP; break;
+ case 0x227: usage->code = KEY_REFRESH; break;
+ case 0x22a: usage->code = KEY_BOOKMARKS; break;
+
+ default: usage->code = KEY_UNKNOWN; break;
+
+ }
+
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ break;
+
+ default:
+ unknown:
+
+ if (field->report_size == 1) {
+
+ if (field->report->type == HID_OUTPUT_REPORT) {
+ usage->code = LED_MISC;
+ usage->type = EV_LED; bit = input->ledbit; max = LED_MAX;
+ break;
+ }
+
+ usage->code = BTN_MISC;
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ break;
+ }
+
+ if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+ usage->code = REL_MISC;
+ usage->type = EV_REL; bit = input->relbit; max = REL_MAX;
+ break;
+ }
+
+ usage->code = ABS_MISC;
+ usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
+ break;
+ }
+
+ set_bit(usage->type, input->evbit);
+
+ while (usage->code <= max && test_and_set_bit(usage->code, bit)) {
+ usage->code = find_next_zero_bit(bit, max + 1, usage->code);
+ }
+
+ if (usage->code > max) return;
+
+ if (usage->type == EV_ABS) {
+ int a = field->logical_minimum;
+ int b = field->logical_maximum;
+
+ input->absmin[usage->code] = a;
+ input->absmax[usage->code] = b;
+ input->absfuzz[usage->code] = (b - a) >> 8;
+ input->absflat[usage->code] = (b - a) >> 4;
+ }
+
+ if (usage->hat_min != usage->hat_max) {
+ int i;
+ for (i = usage->code; i < usage->code + 2 && i <= max; i++) {
+ input->absmax[i] = 1;
+ input->absmin[i] = -1;
+ input->absfuzz[i] = 0;
+ input->absflat[i] = 0;
+ }
+ set_bit(usage->code + 1, input->absbit);
+ }
+}
+
+void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
+{
+ struct input_dev *input = &hid->input;
+ int *quirks = &hid->quirks;
+
+ if (usage->hat_min != usage->hat_max) {
+ value = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1;
+ if (value < 0 || value > 8) value = 0;
+ input_event(input, usage->type, usage->code , hid_hat_to_axis[value].x);
+ input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[value].y);
+ return;
+ }
+
+ if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
+ *quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
+ return;
+ }
+
+ if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
+ if (value) {
+ input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
+ return;
+ }
+ input_event(input, usage->type, usage->code, 0);
+ input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
+ return;
+ }
+
+ if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
+ int a = field->logical_minimum;
+ int b = field->logical_maximum;
+ input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
+ }
+
+ if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UKNOWN */
+ return;
+
+ input_event(input, usage->type, usage->code, value);
+
+ if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
+ input_event(input, usage->type, usage->code, 0);
+}
+
+static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct hid_device *hid = dev->private;
+ struct hid_field *field = NULL;
+ int offset;
+
+ if ((offset = hid_find_field(hid, type, code, &field)) == -1) {
+ warn("event field not found");
+ return -1;
+ }
+
+ hid_set_field(field, offset, value);
+ hid_write_report(hid, field->report);
+
+ return 0;
+}
+
+static int hidinput_open(struct input_dev *dev)
+{
+ struct hid_device *hid = dev->private;
+ return hid_open(hid);
+}
+
+static void hidinput_close(struct input_dev *dev)
+{
+ struct hid_device *hid = dev->private;
+ hid_close(hid);
+}
+
+/*
+ * Register the input device; print a message.
+ * Configure the input layer interface
+ * Read all reports and initalize the absoulte field values.
+ */
+
+int hidinput_connect(struct hid_device *hid)
+{
+ struct usb_device *dev = hid->dev;
+ struct hid_report_enum *report_enum;
+ struct hid_report *report;
+ struct list_head *list;
+ int i, j, k;
+
+ for (i = 0; i < hid->maxapplication; i++)
+ if (IS_INPUT_APPLICATION(hid->application[i]))
+ break;
+
+ if (i == hid->maxapplication)
+ return -1;
+
+ hid->input.private = hid;
+ hid->input.event = hidinput_input_event;
+ hid->input.open = hidinput_open;
+ hid->input.close = hidinput_close;
+
+ hid->input.name = hid->name;
+ hid->input.idbus = BUS_USB;
+ hid->input.idvendor = dev->descriptor.idVendor;
+ hid->input.idproduct = dev->descriptor.idProduct;
+ hid->input.idversion = dev->descriptor.bcdDevice;
+
+ for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+ report_enum = hid->report_enum + k;
+ list = report_enum->report_list.next;
+ while (list != &report_enum->report_list) {
+ report = (struct hid_report *) list;
+ for (i = 0; i < report->maxfield; i++)
+ for (j = 0; j < report->field[i]->maxusage; j++)
+ hidinput_configure_usage(hid, report->field[i], report->field[i]->usage + j);
+ list = list->next;
+ }
+ }
+
+ input_register_device(&hid->input);
+
+ return 0;
+}
+
+void hidinput_disconnect(struct hid_device *hid)
+{
+ input_unregister_device(&hid->input);
+}
diff --git a/src/hiddev.c b/src/hiddev.c
new file mode 100644
index 0000000..9adda3c
--- /dev/null
+++ b/src/hiddev.c
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2001 Paul Stewart
+ * Copyright (c) 2001 Vojtech Pavlik
+ *
+ * HID char devices, giving access to raw HID device events.
+ *
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
+ */
+
+#include <linux/autoconf.h>
+#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+# define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#define HIDDEV_MINOR_BASE 96
+#define HIDDEV_MINORS 16
+#define HIDDEV_BUFFER_SIZE 64
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include "hid.h"
+#include <linux/hiddev.h>
+
+struct hiddev {
+ int exist;
+ int open;
+ int minor;
+ wait_queue_head_t wait;
+ devfs_handle_t devfs;
+ struct hid_device *hid;
+ struct hiddev_list *list;
+};
+
+struct hiddev_list {
+ struct hiddev_event buffer[HIDDEV_BUFFER_SIZE];
+ int head;
+ int tail;
+ struct fasync_struct *fasync;
+ struct hiddev *hiddev;
+ struct hiddev_list *next;
+};
+
+static struct hiddev *hiddev_table[HIDDEV_MINORS];
+static devfs_handle_t hiddev_devfs_handle;
+
+/*
+ * Find a report, given the report's type and ID. The ID can be specified
+ * indirectly by REPORT_ID_FIRST (which returns the first report of the given
+ * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
+ * given type which follows old_id.
+ */
+static struct hid_report *
+hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
+{
+ struct hid_report_enum *report_enum;
+ struct list_head *list;
+
+ if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
+ rinfo->report_type > HID_REPORT_TYPE_MAX) return NULL;
+
+ report_enum = hid->report_enum +
+ (rinfo->report_type - HID_REPORT_TYPE_MIN);
+ if ((rinfo->report_id & ~HID_REPORT_ID_MASK) != 0) {
+ switch (rinfo->report_id & ~HID_REPORT_ID_MASK) {
+ case HID_REPORT_ID_FIRST:
+ list = report_enum->report_list.next;
+ if (list == &report_enum->report_list) return NULL;
+ rinfo->report_id = ((struct hid_report *) list)->id;
+ break;
+
+ case HID_REPORT_ID_NEXT:
+ list = (struct list_head *)
+ report_enum->report_id_hash[rinfo->report_id &
+ HID_REPORT_ID_MASK];
+ if (list == NULL) return NULL;
+ list = list->next;
+ if (list == &report_enum->report_list) return NULL;
+ rinfo->report_id = ((struct hid_report *) list)->id;
+ break;
+
+ default:
+ return NULL;
+ }
+ }
+
+ return report_enum->report_id_hash[rinfo->report_id];
+}
+
+/*
+ * Perform an exhaustive search of the report table for a usage, given its
+ * type and usage id.
+ */
+static struct hid_field *
+hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
+{
+ int i, j;
+ struct hid_report *report;
+ struct hid_report_enum *report_enum;
+ struct list_head *list;
+ struct hid_field *field;
+
+ if (uref->report_type < HID_REPORT_TYPE_MIN ||
+ uref->report_type > HID_REPORT_TYPE_MAX) return NULL;
+
+ report_enum = hid->report_enum +
+ (uref->report_type - HID_REPORT_TYPE_MIN);
+ list = report_enum->report_list.next;
+ while (list != &report_enum->report_list) {
+ report = (struct hid_report *) list;
+ for (i = 0; i < report->maxfield; i++) {
+ field = report->field[i];
+ for (j = 0; j < field->maxusage; j++) {
+ if (field->usage[j].hid == uref->usage_code) {
+ uref->report_id = report->id;
+ uref->field_index = i;
+ uref->usage_index = j;
+ return field;
+ }
+ }
+ }
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * This is where hid.c calls into hiddev to pass an event that occurred over
+ * the interrupt pipe
+ */
+void hiddev_hid_event(struct hid_device *hid, unsigned int usage, int value)
+{
+ struct hiddev *hiddev = hid->hiddev;
+ struct hiddev_list *list = hiddev->list;
+
+ while (list) {
+ list->buffer[list->head].hid = usage;
+ list->buffer[list->head].value = value;
+ list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1);
+
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+
+ list = list->next;
+ }
+
+ wake_up_interruptible(&hiddev->wait);
+}
+
+/*
+ * fasync file op
+ */
+static int hiddev_fasync(int fd, struct file *file, int on)
+{
+ int retval;
+ struct hiddev_list *list = file->private_data;
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+/*
+ * De-allocate a hiddev structure
+ */
+static void hiddev_cleanup(struct hiddev *hiddev)
+{
+ devfs_unregister(hiddev->devfs);
+ hiddev_table[hiddev->minor] = NULL;
+ kfree(hiddev);
+}
+
+/*
+ * release file op
+ */
+static int hiddev_release(struct inode * inode, struct file * file)
+{
+ struct hiddev_list *list = file->private_data;
+ struct hiddev_list **listptr;
+
+ lock_kernel();
+ listptr = &list->hiddev->list;
+ hiddev_fasync(-1, file, 0);
+
+ while (*listptr && (*listptr != list))
+ listptr = &((*listptr)->next);
+ *listptr = (*listptr)->next;
+
+ if (!--list->hiddev->open) {
+ if (list->hiddev->exist)
+ hid_close(list->hiddev->hid);
+ else
+ hiddev_cleanup(list->hiddev);
+ }
+
+ kfree(list);
+ unlock_kernel();
+
+ return 0;
+}
+
+/*
+ * open file op
+ */
+static int hiddev_open(struct inode * inode, struct file * file) {
+ struct hiddev_list *list;
+
+ int i = MINOR(inode->i_rdev) - HIDDEV_MINOR_BASE;
+
+ if (i >= HIDDEV_MINORS || !hiddev_table[i])
+ return -ENODEV;
+
+ if (!(list = kmalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(list, 0, sizeof(struct hiddev_list));
+
+ list->hiddev = hiddev_table[i];
+ list->next = hiddev_table[i]->list;
+ hiddev_table[i]->list = list;
+
+ file->private_data = list;
+
+ if (!list->hiddev->open++)
+ if (list->hiddev->exist)
+ hid_open(hiddev_table[i]->hid);
+
+ return 0;
+}
+
+/*
+ * "write" file op
+ */
+static ssize_t hiddev_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+/*
+ * "read" file op
+ */
+static ssize_t hiddev_read(struct file * file, char * buffer, size_t count,
+ loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct hiddev_list *list = file->private_data;
+ int retval = 0;
+
+ if (list->head == list->tail) {
+
+ add_wait_queue(&list->hiddev->wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list->head == list->tail) {
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!list->hiddev->exist) {
+ retval = -EIO;
+ break;
+ }
+
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&list->hiddev->wait, &wait);
+ }
+
+ if (retval)
+ return retval;
+
+ while (list->head != list->tail && retval + sizeof(struct hiddev_event) <= count) {
+ if (copy_to_user(buffer + retval, list->buffer + list->tail,
+ sizeof(struct hiddev_event))) return -EFAULT;
+ list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
+ retval += sizeof(struct hiddev_event);
+ }
+
+ return retval;
+}
+
+/*
+ * "poll" file op
+ * No kernel lock - fine
+ */
+static unsigned int hiddev_poll(struct file *file, poll_table *wait)
+{
+ struct hiddev_list *list = file->private_data;
+ poll_wait(file, &list->hiddev->wait, wait);
+ if (list->head != list->tail)
+ return POLLIN | POLLRDNORM;
+ if (!list->hiddev->exist)
+ return POLLERR | POLLHUP;
+ return 0;
+}
+
+#define GET_TIMEOUT 3
+#define SET_TIMEOUT 3
+
+/*
+ * "ioctl" file op
+ */
+static int hiddev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hiddev_list *list = file->private_data;
+ struct hiddev *hiddev = list->hiddev;
+ struct hid_device *hid = hiddev->hid;
+ struct usb_device *dev = hid->dev;
+ struct hiddev_report_info rinfo;
+ struct hiddev_usage_ref uref;
+ struct hid_report *report;
+ struct hid_field *field;
+
+ if (!hiddev->exist) return -EIO;
+
+ switch (cmd) {
+
+ case HIDIOCGVERSION:
+ return put_user(HID_VERSION, (int *) arg);
+
+ case HIDIOCAPPLICATION:
+ if (arg < 0 || arg >= hid->maxapplication)
+ return -EINVAL;
+ return hid->application[arg];
+
+ case HIDIOCGDEVINFO:
+ {
+ struct hiddev_devinfo dinfo;
+ dinfo.bustype = BUS_USB;
+ dinfo.busnum = dev->bus->busnum;
+ dinfo.devnum = dev->devnum;
+ dinfo.ifnum = hid->ifnum;
+ dinfo.vendor = dev->descriptor.idVendor;
+ dinfo.product = dev->descriptor.idProduct;
+ dinfo.version = dev->descriptor.bcdDevice;
+ dinfo.num_applications = hid->maxapplication;
+ return copy_to_user((void *) arg, &dinfo, sizeof(dinfo));
+ }
+
+ case HIDIOCGSTRING:
+ {
+ int idx, len;
+ char *buf;
+
+ if (get_user(idx, (int *) arg))
+ return -EFAULT;
+
+ if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *) (arg+sizeof(int)), buf, len+1)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ kfree(buf);
+
+ return len;
+ }
+
+ case HIDIOCINITREPORT:
+
+ hid_init_reports(hid);
+
+ return 0;
+
+ case HIDIOCGREPORT:
+ if (copy_from_user(&rinfo, (void *) arg, sizeof(rinfo)))
+ return -EFAULT;
+
+ if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
+ return -EINVAL;
+
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ hid_read_report(hid, report);
+
+ return 0;
+
+ case HIDIOCSREPORT:
+ if (copy_from_user(&rinfo, (void *) arg, sizeof(rinfo)))
+ return -EFAULT;
+
+ if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
+ return -EINVAL;
+
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ hid_write_report(hid, report);
+
+ return 0;
+
+ case HIDIOCGREPORTINFO:
+ if (copy_from_user(&rinfo, (void *) arg, sizeof(rinfo)))
+ return -EFAULT;
+
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ rinfo.num_fields = report->maxfield;
+
+ return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));
+
+ case HIDIOCGFIELDINFO:
+ {
+ struct hiddev_field_info finfo;
+ if (copy_from_user(&finfo, (void *) arg, sizeof(finfo)))
+ return -EFAULT;
+ rinfo.report_type = finfo.report_type;
+ rinfo.report_id = finfo.report_id;
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ if (finfo.field_index >= report->maxfield)
+ return -EINVAL;
+
+ field = report->field[finfo.field_index];
+ memset(&finfo, 0, sizeof(finfo));
+ finfo.report_type = rinfo.report_type;
+ finfo.report_id = rinfo.report_id;
+ finfo.field_index = field->report_count - 1;
+ finfo.maxusage = field->maxusage;
+ finfo.flags = field->flags;
+ finfo.physical = field->physical;
+ finfo.logical = field->logical;
+ finfo.application = field->application;
+ finfo.logical_minimum = field->logical_minimum;
+ finfo.logical_maximum = field->logical_maximum;
+ finfo.physical_minimum = field->physical_minimum;
+ finfo.physical_maximum = field->physical_maximum;
+ finfo.unit_exponent = field->unit_exponent;
+ finfo.unit = field->unit;
+
+ return copy_to_user((void *) arg, &finfo, sizeof(finfo));
+ }
+
+ case HIDIOCGUCODE:
+ if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
+ return -EFAULT;
+
+ rinfo.report_type = uref.report_type;
+ rinfo.report_id = uref.report_id;
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ if (uref.field_index >= report->maxfield)
+ return -EINVAL;
+
+ field = report->field[uref.field_index];
+ if (uref.usage_index >= field->maxusage)
+ return -EINVAL;
+
+ uref.usage_code = field->usage[uref.usage_index].hid;
+
+ return copy_to_user((void *) arg, &uref, sizeof(uref));
+
+ case HIDIOCGUSAGE:
+ if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
+ return -EFAULT;
+
+ if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
+ field = hiddev_lookup_usage(hid, &uref);
+ if (field == NULL)
+ return -EINVAL;
+ } else {
+ rinfo.report_type = uref.report_type;
+ rinfo.report_id = uref.report_id;
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ if (uref.field_index >= report->maxfield)
+ return -EINVAL;
+
+ field = report->field[uref.field_index];
+ if (uref.usage_index >= field->maxusage)
+ return -EINVAL;
+ }
+
+ uref.value = field->value[uref.usage_index];
+
+ return copy_to_user((void *) arg, &uref, sizeof(uref));
+
+ case HIDIOCSUSAGE:
+ if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
+ return -EFAULT;
+
+ if (uref.report_type == HID_REPORT_TYPE_INPUT)
+ return -EINVAL;
+
+ if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
+ field = hiddev_lookup_usage(hid, &uref);
+ if (field == NULL)
+ return -EINVAL;
+ } else {
+ rinfo.report_type = uref.report_type;
+ rinfo.report_id = uref.report_id;
+ if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+ return -EINVAL;
+
+ if (uref.field_index >= report->maxfield)
+ return -EINVAL;
+
+ field = report->field[uref.field_index];
+ if (uref.usage_index >= field->maxusage)
+ return -EINVAL;
+ }
+
+ field->value[uref.usage_index] = uref.value;
+
+ return 0;
+
+ default:
+
+ if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
+ return -EINVAL;
+
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
+ int len;
+ if (!hid->name) return 0;
+ len = strlen(hid->name) + 1;
+ if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+ return copy_to_user((char *) arg, hid->name, len) ?
+ -EFAULT : len;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct file_operations hiddev_fops = {
+ owner: THIS_MODULE,
+ read: hiddev_read,
+ write: hiddev_write,
+ poll: hiddev_poll,
+ open: hiddev_open,
+ release: hiddev_release,
+ ioctl: hiddev_ioctl,
+ fasync: hiddev_fasync,
+};
+
+/*
+ * This is where hid.c calls us to connect a hid device to the hiddev driver
+ */
+int hiddev_connect(struct hid_device *hid)
+{
+ struct hiddev *hiddev;
+ int minor, i;
+ char devfs_name[16];
+
+ for (i = 0; i < hid->maxapplication; i++)
+ if (!IS_INPUT_APPLICATION(hid->application[i]))
+ break;
+
+ if (i == hid->maxapplication)
+ return -1;
+
+ for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++);
+ if (minor == HIDDEV_MINORS) {
+ printk(KERN_ERR "hiddev: no more free hiddev devices\n");
+ return -1;
+ }
+
+ if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL)))
+ return -1;
+ memset(hiddev, 0, sizeof(struct hiddev));
+
+ init_waitqueue_head(&hiddev->wait);
+
+ hiddev->minor = minor;
+ hiddev_table[minor] = hiddev;
+
+ hiddev->hid = hid;
+ hiddev->exist = 1;
+
+ sprintf(devfs_name, "hiddev%d", minor);
+ hiddev->devfs = devfs_register(hiddev_devfs_handle, devfs_name,
+ DEVFS_FL_DEFAULT, USB_MAJOR,
+ minor + HIDDEV_MINOR_BASE,
+ S_IFCHR | S_IRUGO | S_IWUSR,
+ &hiddev_fops, NULL);
+ hid->minor = minor;
+ hid->hiddev = hiddev;
+
+ return 0;
+}
+
+/*
+ * This is where hid.c calls us to disconnect a hiddev device from the
+ * corresponding hid device (usually because the usb device has disconnected)
+ */
+void hiddev_disconnect(struct hid_device *hid)
+{
+ struct hiddev *hiddev = hid->hiddev;
+
+ hiddev->exist = 0;
+
+ if (hiddev->open) {
+ hid_close(hiddev->hid);
+ wake_up_interruptible(&hiddev->wait);
+ } else {
+ hiddev_cleanup(hiddev);
+ }
+}
+
+/* Currently this driver is a USB driver. It's not a conventional one in
+ * the sense that it doesn't probe at the USB level. Instead it waits to
+ * be connected by HID through the hiddev_connect / hiddev_disconnect
+ * routines. The reason to register as a USB device is to gain part of the
+ * minor number space from the USB major.
+ *
+ * In theory, should the HID code be generalized to more than one physical
+ * medium (say, IEEE 1384), this driver will probably need to register its
+ * own major number, and in doing so, no longer need to register with USB.
+ * At that point the probe routine and hiddev_driver struct below will no
+ * longer be useful.
+ */
+
+
+/* We never attach in this manner, and rely on HID to connect us. This
+ * is why there is no disconnect routine defined in the usb_driver either.
+ */
+static void *hiddev_usbd_probe(struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *hiddev_info)
+{
+ return NULL;
+}
+
+
+static /* const */ struct usb_driver hiddev_driver = {
+ name: "hiddev",
+ probe: hiddev_usbd_probe,
+ fops: &hiddev_fops,
+ minor: HIDDEV_MINOR_BASE
+};
+
+int __init hiddev_init(void)
+{
+ hiddev_devfs_handle =
+ devfs_mk_dir(devfs_find_handle(NULL, "usb", 0, 0, 0, 0), "hid", NULL);
+ usb_register(&hiddev_driver);
+ return 0;
+}
+
+void __exit hiddev_exit(void)
+{
+ devfs_unregister(hiddev_devfs_handle);
+ usb_deregister(&hiddev_driver);
+}
diff --git a/src/usbmouse.c b/src/usbmouse.c
new file mode 100644
index 0000000..b586f0b
--- /dev/null
+++ b/src/usbmouse.c
@@ -0,0 +1,230 @@
+/*
+ * $Id: usbmouse.c,v 1.1 2002/12/23 02:44:46 jjoganic Exp $
+ *
+ * Copyright (c) 1999-2000 Vojtech Pavlik
+ *
+ * USB HIDBP Mouse support
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/autoconf.h>
+#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+# define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.6"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
+#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+struct usb_mouse {
+ signed char data[8];
+ char name[128];
+ struct usb_device *usbdev;
+ struct input_dev dev;
+ struct urb irq;
+ int open;
+};
+
+static void usb_mouse_irq(struct urb *urb)
+{
+ struct usb_mouse *mouse = urb->context;
+ signed char *data = mouse->data;
+ struct input_dev *dev = &mouse->dev;
+
+ if (urb->status) return;
+
+ input_report_key(dev, BTN_LEFT, data[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
+ input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
+ input_report_key(dev, BTN_SIDE, data[0] & 0x08);
+ input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
+
+ input_report_rel(dev, REL_X, data[1]);
+ input_report_rel(dev, REL_Y, data[2]);
+ input_report_rel(dev, REL_WHEEL, data[3]);
+}
+
+static int usb_mouse_open(struct input_dev *dev)
+{
+ struct usb_mouse *mouse = dev->private;
+
+ if (mouse->open++)
+ return 0;
+
+ mouse->irq.dev = mouse->usbdev;
+ if (usb_submit_urb(&mouse->irq))
+ return -EIO;
+
+ return 0;
+}
+
+static void usb_mouse_close(struct input_dev *dev)
+{
+ struct usb_mouse *mouse = dev->private;
+
+ if (!--mouse->open)
+ usb_unlink_urb(&mouse->irq);
+}
+
+static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *id)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_mouse *mouse;
+ int pipe, maxp;
+ char *buf;
+
+ iface = &dev->actconfig->interface[ifnum];
+ interface = &iface->altsetting[iface->act_altsetting];
+
+ if (interface->bNumEndpoints != 1) return NULL;
+
+ endpoint = interface->endpoint + 0;
+ if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
+ if ((endpoint->bmAttributes & 3) != 3) return NULL;
+
+ /* wacom tablets match... */
+ if (dev->descriptor.idVendor == 0x056a)
+ {
+#ifdef __JEJ_DEBUG
+ printk(KERN_INFO __FILE__ ": usb_mouse_probe: ignoring wacom\n");
+#endif
+ return NULL;
+ }
+
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);
+
+ if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL;
+ memset(mouse, 0, sizeof(struct usb_mouse));
+
+ mouse->usbdev = dev;
+
+ mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+ mouse->dev.relbit[0] |= BIT(REL_WHEEL);
+
+ mouse->dev.private = mouse;
+ mouse->dev.open = usb_mouse_open;
+ mouse->dev.close = usb_mouse_close;
+
+ mouse->dev.name = mouse->name;
+ mouse->dev.idbus = BUS_USB;
+ mouse->dev.idvendor = dev->descriptor.idVendor;
+ mouse->dev.idproduct = dev->descriptor.idProduct;
+ mouse->dev.idversion = dev->descriptor.bcdDevice;
+
+ if (!(buf = kmalloc(63, GFP_KERNEL))) {
+ kfree(mouse);
+ return NULL;
+ }
+
+ if (dev->descriptor.iManufacturer &&
+ usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
+ strcat(mouse->name, buf);
+ if (dev->descriptor.iProduct &&
+ usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
+ sprintf(mouse->name, "%s %s", mouse->name, buf);
+
+ if (!strlen(mouse->name))
+ sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x",
+ mouse->dev.idvendor, mouse->dev.idproduct);
+
+ kfree(buf);
+
+ FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp,
+ usb_mouse_irq, mouse, endpoint->bInterval);
+
+ input_register_device(&mouse->dev);
+
+ printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+ mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum);
+
+ return mouse;
+}
+
+static void usb_mouse_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct usb_mouse *mouse = ptr;
+ usb_unlink_urb(&mouse->irq);
+ input_unregister_device(&mouse->dev);
+ kfree(mouse);
+}
+
+static struct usb_device_id usb_mouse_id_table [] = {
+ { USB_INTERFACE_INFO(3, 1, 2) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
+
+static struct usb_driver usb_mouse_driver = {
+ name: "usb_mouse",
+ probe: usb_mouse_probe,
+ disconnect: usb_mouse_disconnect,
+ id_table: usb_mouse_id_table,
+};
+
+static int __init usb_mouse_init(void)
+{
+#ifdef __JEJ_DEBUG
+ printk(KERN_INFO __FILE__ ": usb_mouse_init (MODIFIED DEBUG)\n");
+#endif
+ usb_register(&usb_mouse_driver);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return 0;
+}
+
+static void __exit usb_mouse_exit(void)
+{
+ usb_deregister(&usb_mouse_driver);
+}
+
+module_init(usb_mouse_init);
+module_exit(usb_mouse_exit);
diff --git a/src/wacdump.c b/src/wacdump.c
new file mode 100644
index 0000000..241baee
--- /dev/null
+++ b/src/wacdump.c
@@ -0,0 +1,716 @@
+/*****************************************************************************
+** wacdump.c
+**
+** Copyright (C) 2002 - John E. Joganic
+**
+** 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 Lesser 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.
+**
+** REVISION HISTORY
+** 2002-12-21 0.3.4 - changed to FILE* to file descriptors
+** 2002-12-17 0.3.3 - split ncurses from main file to avoid namespace
+** collision in linux/input.h
+**
+****************************************************************************/
+
+#include "wacscrn.h"
+#include "wacserial.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/input.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+
+#define WACDUMP_VER "wacdump v0.3.4"
+
+/* from linux/input.h */
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
+#define LONG(x) ((x)/BITS_PER_LONG)
+
+static int InitUSB(int fd);
+static void FetchUSB(int fd);
+static int InitSerial(WACOMTABLET hTablet);
+void FetchSerial(WACOMTABLET hTablet);
+static const char* GetTypeCode(unsigned short wCode);
+static const char* GetRelCode(unsigned short wCode);
+static const char* GetAbsCode(unsigned short wCode);
+static const char* GetKeyCode(unsigned short wCode);
+static void DisplayEv(unsigned short wCode);
+static void DisplayRel(unsigned short wCode);
+static void DisplayAbs(unsigned short wCode);
+static void DisplayKey(unsigned short wCode);
+static void DisplayToolSerial(void);
+
+static void DisplaySerialValue(unsigned int uField);
+static void DisplaySerialButton(unsigned int uCode);
+
+struct EV_STATE
+{
+ int bValid;
+ int nRow;
+ struct input_event ev;
+};
+
+struct REL_STATE
+{
+ int bValid;
+ int nRow, nCol;
+ int nValue;
+};
+
+struct ABS_STATE
+{
+ int bValid;
+ int nRow, nCol;
+ int nValue, nMin, nMax;
+};
+
+struct KEY_STATE
+{
+ int bValid;
+ int nRow, nCol;
+ int nValue;
+};
+
+struct SERIAL_STATE
+{
+ int nRow;
+ int nSerial;
+};
+
+ struct EV_STATE gEvState[EV_MAX];
+ struct ABS_STATE gRelState[REL_MAX];
+ struct ABS_STATE gAbsState[ABS_MAX];
+ struct KEY_STATE gKeyState[KEY_MAX];
+ struct SERIAL_STATE gSerialState;
+
+static int InitUSB(int fd)
+{
+ int nCnt, nItem, nRow=0;
+ unsigned short i;
+ char chBuf[256];
+ short sID[4];
+ int nAbs[5];
+ unsigned long evbits[NBITS(EV_MAX)];
+ unsigned long absbits[NBITS(ABS_MAX)];
+ unsigned long relbits[NBITS(REL_MAX)];
+ unsigned long keybits[NBITS(KEY_MAX)];
+
+ /* Identify program and version */
+ wacscrn_standout();
+ for (i=0; i<80; ++i) wacscrn_output(nRow,i," ");
+ wacscrn_output(nRow,0,WACDUMP_VER);
+ wacscrn_normal();
+ nRow += 2;
+
+ /* Get device name and id */
+ if (ioctl(fd,EVIOCGNAME(sizeof(chBuf)),chBuf) < 0)
+ strncpy(chBuf,"Unknown Device Name",sizeof(chBuf));
+ wacscrn_output(nRow,0,chBuf);
+ if (ioctl(fd,EVIOCGID,sID) < 0)
+ strncpy(chBuf,"No ID",sizeof(chBuf));
+ else
+ snprintf(chBuf,sizeof(chBuf),"bus=%X, vndr=%X, prd=%X, ver=%X",
+ sID[0], sID[1], sID[2], sID[3]);
+ wacscrn_output(nRow,40,chBuf);
+ nRow += 2;
+
+ /* Get event types supported */
+ nItem = 0;
+ memset(gEvState,0,sizeof(gEvState));
+ nCnt = ioctl(fd,EVIOCGBIT(0 /*EV*/,sizeof(evbits)),evbits);
+ if (nCnt < 0) { perror("Failed to CGBIT"); return 1; }
+ assert(nCnt == sizeof(evbits));
+ for (i=0; i<EV_MAX; ++i)
+ {
+ if (evbits[LONG(i)] & BIT(i))
+ {
+ gEvState[i].bValid = 1;
+ gEvState[i].nRow = nRow + nItem;
+ DisplayEv(i);
+ ++nItem;
+ }
+ }
+ nRow += nItem + 1;
+
+ /* get absolute event types supported, ranges, and immediate values */
+ nItem = 0;
+ memset(gAbsState,0,sizeof(gAbsState));
+ if (evbits[LONG(EV_ABS)] & BIT(EV_ABS))
+ {
+ nCnt = ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits);
+ if (nCnt < 0) { perror("Failed to CGBIT"); return 1; }
+ assert(nCnt == sizeof(absbits));
+ for (i=0; i<ABS_MAX; ++i)
+ {
+ if (absbits[LONG(i)] & BIT(i))
+ {
+ ioctl(fd, EVIOCGABS(i), nAbs);
+ gAbsState[i].bValid = 1;
+ gAbsState[i].nValue = nAbs[0];
+ gAbsState[i].nMin = nAbs[1];
+ gAbsState[i].nMax = nAbs[2];
+ gAbsState[i].nRow = nRow + nItem / 2;
+ gAbsState[i].nCol = nItem % 2;
+ DisplayAbs(i);
+ ++nItem;
+ }
+ }
+ }
+ nRow += ((nItem + 1) / 2) + 1;
+
+ /* get relative event types supported, ranges, and immediate values */
+ nItem = 0;
+ memset(gRelState,0,sizeof(gRelState));
+ if (evbits[LONG(EV_REL)] & BIT(EV_REL))
+ {
+ nCnt = ioctl(fd,EVIOCGBIT(EV_REL,sizeof(relbits)),relbits);
+ if (nCnt < 0) { perror("Failed to CGBIT"); return 1; }
+ assert(nCnt == sizeof(relbits));
+ for (i=0; i<REL_MAX; ++i)
+ {
+ if (relbits[LONG(i)] & BIT(i))
+ {
+ gRelState[i].bValid = 1;
+ gRelState[i].nValue = 0; /* start at zero */
+ gRelState[i].nRow = nRow + nItem / 4;
+ gRelState[i].nCol = nItem % 4;
+ DisplayRel(i);
+ ++nItem;
+ }
+ }
+ }
+ nRow += ((nItem + 1) / 4) + 1;
+
+ /* Get serial */
+ gSerialState.nRow = nRow;
+ gSerialState.nSerial = 0;
+ nRow += 2;
+
+ /* get key event types supported */
+ nItem = 0;
+ memset(gKeyState,0,sizeof(gKeyState));
+ if (evbits[LONG(EV_KEY)] & BIT(EV_KEY))
+ {
+ nCnt = ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits);
+ if (nCnt < 0) { perror("Failed to CGBIT"); return 1; }
+ assert(nCnt == sizeof(keybits));
+ for (i=0; i<KEY_MAX; ++i)
+ {
+ if (keybits[LONG(i)] & BIT(i))
+ {
+ /* fetching key state not possible :( */
+ gKeyState[i].bValid = 1;
+ gKeyState[i].nRow = nRow + nItem / 4;
+ gKeyState[i].nCol = nItem % 4;
+ DisplayKey(i);
+ ++nItem;
+ }
+ }
+ }
+ nRow += ((nItem + 3) / 4) + 1;
+ return 0;
+}
+
+void DisplayEv(unsigned short wCode)
+{
+ static char xchBuf[256];
+ if ((wCode >= EV_MAX) || !gEvState[wCode].bValid)
+ {
+ snprintf(xchBuf,sizeof(xchBuf),"Bad EV Code: 0x%X",wCode);
+ wacscrn_output(23,0,xchBuf);
+ return;
+ }
+
+ snprintf(xchBuf,sizeof(xchBuf),"%4s=%08lX.%08lX %04X %04X %08X",
+ GetTypeCode(wCode),
+ gEvState[wCode].ev.time.tv_sec,
+ gEvState[wCode].ev.time.tv_usec,
+ gEvState[wCode].ev.type,
+ gEvState[wCode].ev.code,
+ gEvState[wCode].ev.value);
+ wacscrn_output(gEvState[wCode].nRow,0,xchBuf);
+}
+
+
+void DisplayRel(unsigned short wCode)
+{
+ static char xchBuf[256];
+ if ((wCode >= REL_MAX) || !gRelState[wCode].bValid)
+ {
+ snprintf(xchBuf,sizeof(xchBuf),"Bad REL Code: 0x%X",wCode);
+ wacscrn_output(24,0,xchBuf);
+ return;
+ }
+
+ snprintf(xchBuf,sizeof(xchBuf),"%8s=%+06d",
+ GetRelCode(wCode), gRelState[wCode].nValue);
+
+ wacscrn_output(gRelState[wCode].nRow,gRelState[wCode].nCol*20,xchBuf);
+}
+
+void DisplayAbs(unsigned short wCode)
+{
+ static char xchBuf[256];
+ if ((wCode >= ABS_MAX) || !gAbsState[wCode].bValid)
+ {
+ snprintf(xchBuf,sizeof(xchBuf),"Bad ABS Code: 0x%X",wCode);
+ wacscrn_output(24,0,xchBuf);
+ return;
+ }
+
+ snprintf(xchBuf,sizeof(xchBuf),"%8s=%+06d (%+06d .. %+06d)",
+ GetAbsCode(wCode),
+ gAbsState[wCode].nValue,
+ gAbsState[wCode].nMin,
+ gAbsState[wCode].nMax);
+ wacscrn_output(gAbsState[wCode].nRow,gAbsState[wCode].nCol*40,xchBuf);
+}
+
+static void DisplayToolSerial(void)
+{
+ static char xchBuf[256];
+ snprintf(xchBuf,sizeof(xchBuf)," SERIAL=%08X",gSerialState.nSerial);
+ wacscrn_output(gSerialState.nRow,0,xchBuf);
+}
+
+static void DisplayKey(unsigned short wCode)
+{
+ int bDown;
+ static char xchBuf[256];
+ if ((wCode >= KEY_MAX) || !gKeyState[wCode].bValid)
+ {
+ snprintf(xchBuf,sizeof(xchBuf),"Bad Key Code: 0x%X",wCode);
+ wacscrn_output(24,0,xchBuf);
+ return;
+ }
+
+ bDown = gKeyState[wCode].nValue ? 1 : 0;
+
+ snprintf(xchBuf,sizeof(xchBuf),"%8s=%s",
+ GetKeyCode(wCode), bDown ? "DOWN" : " ");
+
+ if (bDown) wacscrn_standout();
+ wacscrn_output(gKeyState[wCode].nRow,gKeyState[wCode].nCol*20,xchBuf);
+ wacscrn_normal();
+}
+
+static const char* GetTypeCode(unsigned short wCode)
+{
+ static const char* xszEv[] =
+ {
+ "RST", "KEY", "REL", "ABS", "MSC"
+ };
+
+ return (wCode >= (sizeof(xszEv)/sizeof(*xszEv))) ?
+ "EV?" : xszEv[wCode];
+}
+
+static const char* GetRelCode(unsigned short wCode)
+{
+ static const char* xszRel[] =
+ {
+ "X", "Y", "Z", "0x03", "0x04", "0x05", "HWHEEL",
+ "DIAL", "WHEEL", "MISC"
+ };
+
+ return (wCode > REL_MISC) ? "KEY?" : xszRel[wCode];
+}
+
+static const char* GetAbsCode(unsigned short wCode)
+{
+ static const char* xszAbs[] =
+ {
+ "X", "Y", "Z", "RX", "RY", "RZ",
+ "THROTTLE", "RUDDER", "WHEEL", "GAS", "BRAKE",
+ "0x0B", "0x0C", "0x0D", "0x0E", "0x0F",
+ "HAT0X", "HAT0Y", "HAT1X", "HAT1Y",
+ "HAT2X", "HAT2Y", "HAY3X", "HAT3Y",
+ "PRESSURE", "DISTANCE", "TILT_X", "TILT_Y",
+ "MISC"
+ };
+
+ return (wCode > ABS_MISC) ? "KEY?" : xszAbs[wCode];
+}
+
+static const char* GetKeyCode(unsigned short wCode)
+{
+ static const char* xszMouseKey[] =
+ {
+ "LEFT", "RIGHT", "MIDDLE", "SIDE", "EXTRA", "FORWARD",
+ "BACK"
+ };
+
+ static const char* xszToolKey[] =
+ {
+ "PEN", "RUBBER", "BRUSH", "PENCIL", "AIR",
+ "FINGER", "MOUSE", "LENS",
+ "0x148", "0x149", "TOUCH", "STYLUS", "STYLUS2"
+ };
+
+ static char xchBuf[16];
+
+ if ((wCode >= BTN_LEFT) && (wCode <= BTN_BACK))
+ return xszMouseKey[wCode - BTN_LEFT];
+
+ if ((wCode >= BTN_TOOL_PEN) && (wCode <= BTN_STYLUS2))
+ return xszToolKey[wCode - BTN_TOOL_PEN];
+
+ snprintf(xchBuf,sizeof(xchBuf),"K_%X",wCode);
+ return xchBuf;
+}
+
+void Usage(void)
+{
+ fprintf(stderr,"Usage: wacdump [-d device]\n"
+ " -?, -h, --help - usage\n"
+ " -d, --device device - use specified device\n");
+}
+
+static void termscr(void)
+{
+ wacscrn_term();
+}
+
+static void FetchUSB(int fd)
+{
+ int nCnt, nXfer;
+ struct input_event ev;
+
+ while (1)
+ {
+ /* read one whole event record */
+ nCnt=0;
+ while (nCnt < sizeof(ev))
+ {
+ nXfer = read(fd,((char*)&ev)+nCnt,sizeof(ev)-nCnt);
+ if (nXfer == 0) break;
+ nCnt += nXfer;
+ }
+
+ if (ev.type < EV_MAX)
+ {
+ gEvState[ev.type].ev = ev;
+ DisplayEv(ev.type);
+ }
+
+ if (ev.type == EV_MSC)
+ {
+ gSerialState.nSerial = ev.value;
+ DisplayToolSerial();
+ }
+ else if ((ev.type == EV_KEY) && (ev.code < KEY_MAX))
+ {
+ gKeyState[ev.code].nValue = ev.value;
+ DisplayKey(ev.code);
+ }
+ else if ((ev.type == EV_ABS) && (ev.code < ABS_MAX))
+ {
+ gAbsState[ev.code].nValue = ev.value;
+ DisplayAbs(ev.code);
+ }
+ else if ((ev.type == EV_REL) && (ev.code < REL_MAX))
+ {
+ gRelState[ev.code].nValue += ev.value;
+ DisplayRel(ev.code);
+ }
+
+ wacscrn_refresh();
+ }
+}
+
+static int InitSerial(WACOMTABLET hTablet)
+{
+ int i, nCaps, nItem, nRow=0;
+ int nMajor, nMinor, nRelease;
+ char chBuf[256];
+ WACOMMODEL model;
+ WACOMSTATE min, max;
+ const char* pszName;
+
+ /* Identify program and version */
+ wacscrn_standout();
+ for (i=0; i<80; ++i) wacscrn_output(nRow,i," ");
+ wacscrn_output(nRow,0,WACDUMP_VER);
+ wacscrn_normal();
+ nRow += 2;
+
+ /* Get device name, ROM, and model */
+ model = WacomGetModel(hTablet);
+ pszName = WacomGetModelName(hTablet);
+ WacomGetRomVersion(hTablet,&nMajor,&nMinor,&nRelease);
+ wacscrn_output(nRow,0,pszName);
+ snprintf(chBuf,sizeof(chBuf),"SERIAL model=0x%X ROM=%d.%d-%d",
+ model, nMajor, nMinor, nRelease);
+ wacscrn_output(nRow,40,chBuf);
+ nRow += 2;
+
+ gEvState[0].nRow = nRow++; /* data */
+ nRow += 2;
+
+ /* get event types supported, ranges, and immediate values */
+ nCaps = WacomGetCapabilities(hTablet);
+ WacomGetRanges(hTablet,&min,&max);
+
+ nItem = 0;
+ for (i=0; i<31; ++i)
+ {
+ if (nCaps & (1<<i))
+ {
+ gAbsState[i].bValid = 1;
+ gAbsState[i].nValue = 0;
+ gAbsState[i].nMin = ((int*)(&min))[i + 1];
+ gAbsState[i].nMax = ((int*)(&max))[i + 1];
+ gAbsState[i].nRow = nRow + nItem / 2;
+ gAbsState[i].nCol = nItem % 2;
+ DisplaySerialValue(i);
+ ++nItem;
+ }
+ }
+ nRow += ((nItem + 1) / 2) + 1;
+
+ /* get key event types */
+ nItem = 0;
+ for (i=0; i<WACOMBUTTON_MAX; ++i)
+ {
+ gKeyState[i].bValid = 1;
+ gKeyState[i].nRow = nRow + nItem / 4;
+ gKeyState[i].nCol = nItem % 4;
+ DisplaySerialButton(i);
+ ++nItem;
+ }
+ nRow += ((nItem + 3) / 4) + 1;
+ return 0;
+}
+
+const char* GetSerialField(unsigned int uField)
+{
+ static const char* xszField[WACOMFIELD_MAX] =
+ {
+ "TOOLTYPE", "SERIAL", "IN_PROX", "BUTTON",
+ "POS_X", "POS_Y", "ROT_Z", "DISTANCE",
+ "PRESSURE", "TILT_X", "TILT_Y", "ABSWHEEL",
+ "RELWHEEL", "THROTTLE"
+ };
+
+ return (uField >= WACOMFIELD_MAX) ? "FIELD?" : xszField[uField];
+}
+
+static void DisplaySerialValue(unsigned int uField)
+{
+ static char xchBuf[256];
+ if ((uField >= WACOMFIELD_MAX) || !gAbsState[uField].bValid)
+ {
+ snprintf(xchBuf,sizeof(xchBuf),"Bad FIELD Code: 0x%X",uField);
+ wacscrn_output(24,0,xchBuf);
+ return;
+ }
+
+ snprintf(xchBuf,sizeof(xchBuf),"%8s=%+06d (%+06d .. %+06d)",
+ GetSerialField(uField),
+ gAbsState[uField].nValue,
+ gAbsState[uField].nMin,
+ gAbsState[uField].nMax);
+ wacscrn_output(gAbsState[uField].nRow,gAbsState[uField].nCol*40,xchBuf);
+}
+
+const char* GetSerialButton(unsigned int uButton)
+{
+ static const char* xszButton[WACOMBUTTON_MAX] =
+ {
+ "LEFT", "MIDDLE", "RIGHT", "EXTRA", "SIDE",
+ "TOUCH", "STYLUS", "STYLUS2"
+ };
+
+ return (uButton >= WACOMBUTTON_MAX) ? "FIELD?" : xszButton[uButton];
+}
+
+
+static void DisplaySerialButton(unsigned int uButton)
+{
+ int bDown;
+ static char xchBuf[256];
+ if ((uButton >= WACOMBUTTON_MAX) || !gKeyState[uButton].bValid)
+ {
+ snprintf(xchBuf,sizeof(xchBuf),"Bad Button Code: 0x%X",uButton);
+ wacscrn_output(24,0,xchBuf);
+ return;
+ }
+
+ bDown = gKeyState[uButton].nValue ? 1 : 0;
+
+ snprintf(xchBuf,sizeof(xchBuf),"%8s=%s",
+ GetSerialButton(uButton), bDown ? "DOWN" : " ");
+
+ if (bDown) wacscrn_standout();
+ wacscrn_output(gKeyState[uButton].nRow,gKeyState[uButton].nCol*20,xchBuf);
+ wacscrn_normal();
+}
+
+void FetchSerial(WACOMTABLET hTablet)
+{
+ char chOut[16];
+ unsigned char uchBuf[16];
+ int i, nLength, nRow=gEvState[0].nRow;
+ WACOMSTATE state;
+
+ while (1)
+ {
+ nLength = WacomReadRaw(hTablet,uchBuf,sizeof(uchBuf));
+ if (nLength < 0) break;
+
+ if (WacomParseData(hTablet,uchBuf,nLength,&state)) break;
+
+ gAbsState[WACOMFIELD_PROXIMITY].nValue = state.nProximity;
+ gAbsState[WACOMFIELD_BUTTONS].nValue = state.nButtons;
+ gAbsState[WACOMFIELD_TOOLTYPE].nValue = state.nToolType;
+ gAbsState[WACOMFIELD_POSITION_X].nValue = state.nPosX;
+ gAbsState[WACOMFIELD_POSITION_Y].nValue = state.nPosY;
+ gAbsState[WACOMFIELD_ROTATION_Z].nValue = state.nRotZ;
+ gAbsState[WACOMFIELD_PRESSURE].nValue = state.nPressure;
+ gAbsState[WACOMFIELD_SERIAL].nValue = state.nSerial;
+ gAbsState[WACOMFIELD_TILT_X].nValue = state.nTiltX;
+ gAbsState[WACOMFIELD_TILT_Y].nValue = state.nTiltY;
+ gAbsState[WACOMFIELD_ABSWHEEL].nValue = state.nAbsWheel;
+ gAbsState[WACOMFIELD_RELWHEEL].nValue += state.nRelWheel;
+ gAbsState[WACOMFIELD_THROTTLE].nValue = state.nThrottle;
+ DisplaySerialValue(WACOMFIELD_PROXIMITY);
+ DisplaySerialValue(WACOMFIELD_BUTTONS);
+ DisplaySerialValue(WACOMFIELD_TOOLTYPE);
+ DisplaySerialValue(WACOMFIELD_POSITION_X);
+ DisplaySerialValue(WACOMFIELD_POSITION_Y);
+ DisplaySerialValue(WACOMFIELD_ROTATION_Z);
+ DisplaySerialValue(WACOMFIELD_PRESSURE);
+ DisplaySerialValue(WACOMFIELD_SERIAL);
+ DisplaySerialValue(WACOMFIELD_TILT_X);
+ DisplaySerialValue(WACOMFIELD_TILT_Y);
+ DisplaySerialValue(WACOMFIELD_ABSWHEEL);
+ DisplaySerialValue(WACOMFIELD_RELWHEEL);
+ DisplaySerialValue(WACOMFIELD_THROTTLE);
+ gKeyState[WACOMBUTTON_TOUCH].nValue = state.nButtons &
+ (1 << WACOMBUTTON_TOUCH) ? 1 : 0;
+ gKeyState[WACOMBUTTON_STYLUS].nValue = state.nButtons &
+ (1 << WACOMBUTTON_STYLUS) ? 1 : 0;
+ gKeyState[WACOMBUTTON_STYLUS2].nValue = state.nButtons &
+ (1 << WACOMBUTTON_STYLUS2) ? 1 : 0;
+ gKeyState[WACOMBUTTON_LEFT].nValue = state.nButtons &
+ (1 << WACOMBUTTON_LEFT) ? 1 : 0;
+ gKeyState[WACOMBUTTON_MIDDLE].nValue = state.nButtons &
+ (1 << WACOMBUTTON_MIDDLE) ? 1 : 0;
+ gKeyState[WACOMBUTTON_RIGHT].nValue = state.nButtons &
+ (1 << WACOMBUTTON_RIGHT) ? 1 : 0;
+ gKeyState[WACOMBUTTON_EXTRA].nValue = state.nButtons &
+ (1 << WACOMBUTTON_EXTRA) ? 1 : 0;
+ gKeyState[WACOMBUTTON_SIDE].nValue = state.nButtons &
+ (1 << WACOMBUTTON_SIDE) ? 1 : 0;
+ DisplaySerialButton(WACOMBUTTON_TOUCH);
+ DisplaySerialButton(WACOMBUTTON_STYLUS);
+ DisplaySerialButton(WACOMBUTTON_STYLUS2);
+ DisplaySerialButton(WACOMBUTTON_LEFT);
+ DisplaySerialButton(WACOMBUTTON_MIDDLE);
+ DisplaySerialButton(WACOMBUTTON_RIGHT);
+ DisplaySerialButton(WACOMBUTTON_EXTRA);
+ DisplaySerialButton(WACOMBUTTON_SIDE);
+
+ for (i=0; i<nLength; ++i)
+ {
+ snprintf(chOut,sizeof(chOut),"%02X",uchBuf[i]);
+ wacscrn_output(nRow,i*3,chOut);
+ }
+ for (i=0; i<nLength; ++i)
+ {
+ snprintf(chOut,sizeof(chOut),"%c",isprint(uchBuf[i]) ?
+ uchBuf[i] : '.');
+ wacscrn_output(nRow,60 + i,chOut);
+ }
+
+ wacscrn_refresh();
+ }
+}
+
+int main(int argc, char** argv)
+{
+ int fd;
+ const char* pszFile = NULL;
+ const char* arg;
+
+ /* parse args */
+ ++argv;
+ while (*argv)
+ {
+ arg = *(argv++);
+ if ((strcmp(arg,"-?") == 0) || (strcmp(arg,"-h") == 0) ||
+ (strcmp(arg,"--help")==0))
+ {
+ Usage();
+ exit(0);
+ }
+ if ((strcmp(arg,"-d") == 0) || (strcmp(arg,"--device") == 0))
+ {
+ arg = *(argv++);
+ if (arg == NULL) { fprintf(stderr,"Missing device\n"); exit(1); }
+ pszFile = arg;
+ }
+ else
+ {
+ fprintf(stderr,"Unknown argument %s\n",arg);
+ exit(1);
+ }
+ }
+
+ /* default device if not specified */
+ if (!pszFile) pszFile = "/dev/input/event0";
+
+ /* open input device */
+ fd = open(pszFile,O_RDONLY);
+ if (fd < 0) { perror("Failed to open device"); exit(1); }
+
+ /* if it is serial, close and try again */
+ if (isatty(fd))
+ {
+ WACOMTABLET hTablet = NULL;
+
+ close(fd);
+ hTablet = WacomOpenSerial(pszFile);
+ if (!hTablet) exit(1);
+
+ /* begin curses window mode */
+ wacscrn_init();
+ atexit(termscr);
+
+ /* get device capabilities, build screen */
+ if (InitSerial(hTablet)) { WacomCloseSerial(hTablet); exit(1); }
+ FetchSerial(hTablet);
+ WacomCloseSerial(hTablet);
+ return 0;
+ }
+
+ /* begin curses window mode */
+ wacscrn_init();
+ atexit(termscr);
+
+ /* get device capabilities, build screen */
+ if (InitUSB(fd)) { close(fd); exit(1); }
+ FetchUSB(fd);
+ close(fd);
+ return 0;
+}
+
diff --git a/src/wacom.c b/src/wacom.c
new file mode 100644
index 0000000..3b6ab22
--- /dev/null
+++ b/src/wacom.c
@@ -0,0 +1,690 @@
+/*
+ * $Id: wacom.c,v 1.1 2002/12/23 02:44:48 jjoganic Exp $
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
+ * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
+ * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
+ * Copyright (c) 2000 James E. Blair <corvus@gnu.org>
+ * Copyright (c) 2000 Daniel Egger <egger@suse.de>
+ * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
+ * Copyright (c) 2002 Christer Nilsson <christer.nilsson@kretskompaniet.se>
+ * Copyright (c) 2002 Ping Cheng <pingc@wacom.com>
+ * Copyright (c) 2002 John Joganic <john@joganic.com>
+ *
+ * USB Wacom Graphire and Intuos tablet support
+ *
+ * Sponsored by SuSE
+ *
+ * ChangeLog:
+ * v0.1 (vp) - Initial release
+ * v0.2 (aba) - Support for all buttons / combinations
+ * v0.3 (vp) - Support for Intuos added
+ * v0.4 (sm) - Support for more Intuos models, menustrip
+ * relative mode, proximity.
+ * v0.5 (vp) - Big cleanup, nifty features removed,
+ * they belong in userspace
+ * v1.8 (vp) - Submit URB only when operating, moved to CVS,
+ * use input_report_key instead of report_btn and
+ * other cleanups
+ * v1.11 (vp) - Add URB ->dev setting for new kernels
+ * v1.11 (jb) - Add support for the 4D Mouse & Lens
+ * v1.12 (de) - Add support for two more inking pen IDs
+ * v1.14 (vp) - Use new USB device id probing scheme.
+ * Fix Wacom Graphire mouse wheel
+ * v1.18 (vp) - Fix mouse wheel direction
+ * Make mouse relative
+ * v1.20 (fl) - Report tool id for Intuos devices
+ * - Multi tools support
+ * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ * - Add PL models support
+ * - Fix Wacom Graphire mouse wheel again
+ * v1.21 (vp) - Removed protocol descriptions
+ * - Added MISC_SERIAL for tool serial numbers
+ * (gb) - Identify version on module load.
+ * v1.21.1 (fl) - added Graphire2 support
+ * v1.21.2 (fl) - added Intuos2 support
+ * - added all the PL ids
+ * v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ * - added smooth filter for Graphire from Peri Hankey
+ * - added PenPartner support from Olaf van Es
+ * - new tool ids from Ole Martin Bjoerndalen
+ * v1.29 (pc) - Add support for more tablets
+ * - Fix pressure reporting
+ * v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ * - Cleanups here and there
+ *
+ * WARNING: THIS IS NOT PART OF THE OFFICIAL KERNEL TREE
+ * THIS IS FOR TESTING PURPOSES
+ *
+ * v1.21.3-j0 - fixed absolute x and y for intuos by John Joganic
+ * v1.21.3-j1 - applied Christer Nilsson's patches for 2.4.20
+ * v1.30.1-j0 - applied Ping Cheng's patches for device ranges and caps
+ * v1.30.1-j1 - updated device ranges for Intuos2 12x12 (0x44)
+ * v1.30.1-j2 - updated device ranges for Intuos2 6x8 (0x42)
+ * v1.30-j0.3.1 - fixed pen identifers, 2D mouse handling
+ * v1.30-j0.3.3 - added volito, thanks to Pasi Savolainen; fixed wheel sign
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/autoconf.h>
+#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
+# define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.30-j0.3.3"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
+#ifndef __JEJ_DEBUG
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver (MODIFIED)"
+#else
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver (MODIFIED-DEBUG)"
+#endif
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define USB_VENDOR_ID_WACOM 0x056a
+
+struct wacom_features {
+ char *name;
+ int pktlen;
+ int x_max;
+ int y_max;
+ int pressure_max;
+ int distance_max;
+ void (*irq)(struct urb *urb);
+ unsigned long evbit;
+ unsigned long absbit;
+ unsigned long relbit;
+ unsigned long btnbit;
+ unsigned long digibit;
+};
+
+struct wacom {
+ signed char data[10];
+ struct input_dev dev;
+ struct usb_device *usbdev;
+ struct urb irq;
+ struct wacom_features *features;
+ int tool[2];
+ int open;
+ __u32 serial[2];
+};
+
+static void wacom_pl_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ unsigned char *data = wacom->data;
+ struct input_dev *dev = &wacom->dev;
+ int prox, pressure;
+
+ if (urb->status) return;
+
+ if (data[0] != 2) {
+ printk(KERN_ERR "wacom_pl_irq: received unknown report #%d\n", data[0]);
+ return;
+ }
+
+ /* Cheng proximity and pressure code */
+ prox = data[1] & 0x40;
+
+ if (prox) {
+ pressure = (signed char) ((data[7] <<1 ) | ((data[4] >> 2) & 1));
+ if ( wacom->features->pressure_max > 350 ) {
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ }
+ pressure += (( wacom->features->pressure_max + 1 )/ 2);
+
+ /*
+ * if going from out of proximity into proximity select between the eraser
+ * and the pen based on the state of the stylus2 button, choose eraser if
+ * pressed else choose pen. if not a proximity change from out to in stay
+ * with the previously selected tool.
+ */
+ if (!wacom->tool[0]) {
+ /* Going into proximity select tool */
+ wacom->tool[1] = (data[4] & 0x20)? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ }
+ else {
+ /* was entered with stylus2 pressed */
+ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20) ) {
+ wacom->tool[1] = BTN_TOOL_PEN;
+ }
+ }
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ }
+ input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */
+
+ input_report_abs(dev, ABS_X, data[3] | ((__u32)data[2] << 7) | ((__u32)(data[1] & 0x03) << 14));
+ input_report_abs(dev, ABS_Y, data[6] | ((__u32)data[5] << 7) | ((__u32)(data[4] & 0x03) << 14));
+ input_report_abs(dev, ABS_PRESSURE, pressure);
+ input_report_key(dev, BTN_TOUCH, data[4] & 0x08);
+ input_report_key(dev, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ }
+ else {
+ /* report proximity-out of a (valid) tool */
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ }
+ input_report_key(dev, wacom->tool[1], prox);
+ }
+
+ wacom->tool[0] = prox; /* Save proximity state */
+ /* end Cheng proximity code */
+
+ input_event(dev, EV_MSC, MSC_SERIAL, 0);
+}
+
+static void wacom_penpartner_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ unsigned char *data = wacom->data;
+ struct input_dev *dev = &wacom->dev;
+ int leftmb = (((signed char)data[6] > -80) && !(data[5] &0x20));
+
+ if (urb->status) return;
+
+ input_report_key(dev, BTN_TOOL_PEN, 1);
+ input_report_abs(dev, ABS_X, data[2] << 8 | data[1]);
+ input_report_abs(dev, ABS_Y, data[4] << 8 | data[3]);
+ input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
+ input_report_key(dev, BTN_TOUCH, leftmb);
+ input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
+
+ input_event(dev, EV_MSC, MSC_SERIAL, leftmb);
+}
+
+static void wacom_graphire_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ unsigned char *data = wacom->data;
+ struct input_dev *dev = &wacom->dev;
+ int x, y;
+
+ if (urb->status) return;
+
+ if (data[0] != 2) {
+ printk(KERN_ERR "wacom_graphire_irq: received unknown report #%d\n", data[0]);
+ return;
+ }
+
+ x = data[2] | ((__u32)data[3] << 8);
+ y = data[4] | ((__u32)data[5] << 8);
+
+ switch ((data[1] >> 5) & 3) {
+
+ case 0: /* Pen */
+ input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80);
+ break;
+
+ case 1: /* Rubber */
+ input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+ break;
+
+ case 2: /* Mouse with wheel */
+ input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
+ case 3: /* Mouse without wheel */
+ input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24);
+ input_report_key(dev, BTN_LEFT, data[1] & 0x01);
+ input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
+ input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
+ input_report_abs(dev, ABS_DISTANCE, data[7]);
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+
+ input_event(dev, EV_MSC, MSC_SERIAL, data[1] & 0x01);
+ return;
+ }
+
+ if (data[1] & 0x80) {
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ }
+
+ input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
+ input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
+ input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
+ input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
+
+ input_event(dev, EV_MSC, MSC_SERIAL, data[1] & 0x01);
+}
+
+static void wacom_intuos_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ unsigned char *data = wacom->data;
+ struct input_dev *dev = &wacom->dev;
+ unsigned int t;
+ int idx;
+
+ if (urb->status) return;
+
+ /* check for valid report */
+ if (data[0] != 2)
+ {
+ #ifndef __JEJ_DEBUG
+ printk(KERN_ERR "wacom_intuos_irq: received unknown report #%d\n",
+ data[0]);
+ #else
+ printk(KERN_ERR "wacom_intuos_irq: unknown report "
+ "%02X %02X %02X %02X\n",data[0], data[1], data[2], data[3]);
+ #endif
+ return;
+ }
+
+ /* tool index */
+ idx = data[1] & 0x01;
+
+ /* Enter report */
+ if ((data[1] & 0xfc) == 0xc0)
+ {
+ /* serial number of the tool */
+ wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 4) +
+ ((__u32)data[4] << 16) + ((__u32)data[5] << 12) +
+ ((__u32)data[6] << 4) + (data[7] >> 4);
+
+ #ifdef __JEJ_DEBUG
+ printk(KERN_INFO "wacom_intuos_irq: tool change 0x%03X\n",
+ (((__u32)data[2] << 4) | (data[3] >> 4)));
+ #endif
+
+ switch ((((__u32)data[2] << 4) | (data[3] >> 4)))
+ {
+ case 0x812: /* Intuos2 ink pen XP-110-00A */
+ case 0x012: /* Inking pen */
+ wacom->tool[idx] = BTN_TOOL_PENCIL; break;
+
+ case 0x822: /* Intuos Pen GP-300E-01H */
+ case 0x852: /* Intuos2 Grip Pen XP-501E-00A */
+ case 0x842: /* added from Cheng */
+ case 0x022:
+ wacom->tool[idx] = BTN_TOOL_PEN; break;
+
+ case 0x832: /* Intuos2 stroke pen XP-120-00A */
+ case 0x032: /* Stroke pen */
+ wacom->tool[idx] = BTN_TOOL_BRUSH; break;
+
+ case 0x007: /* 2D Mouse */
+ case 0x09C: /* ?? Mouse */
+ case 0x094: /* 4D Mouse */
+ wacom->tool[idx] = BTN_TOOL_MOUSE; break;
+
+ case 0x096: /* Lens cursor */
+ wacom->tool[idx] = BTN_TOOL_LENS; break;
+
+ case 0x82a:
+ case 0x85a:
+ case 0x91a:
+ case 0x0fa: /* Eraser */
+ wacom->tool[idx] = BTN_TOOL_RUBBER; break;
+
+ case 0x112: /* Airbrush */
+ wacom->tool[idx] = BTN_TOOL_AIRBRUSH; break;
+
+ default: /* Unknown tool */
+ wacom->tool[idx] = BTN_TOOL_PEN; break;
+ }
+
+ input_report_key(dev, wacom->tool[idx], 1);
+ input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return;
+ }
+
+ /* Exit report */
+ if ((data[1] & 0xfe) == 0x80)
+ {
+ input_report_key(dev, wacom->tool[idx], 0);
+ input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return;
+ }
+
+ input_report_abs(dev, ABS_X, ((__u32)data[2] << 8) | data[3]);
+ input_report_abs(dev, ABS_Y, ((__u32)data[4] << 8) | data[5]);
+ input_report_abs(dev, ABS_DISTANCE, data[9] >> 4);
+
+ /* general pen packet */
+ if ((data[1] & 0xb8) == 0xa0)
+ {
+ t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
+ input_report_abs(dev, ABS_PRESSURE, t);
+ input_report_abs(dev, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+ input_report_key(dev, BTN_STYLUS, data[1] & 2);
+ input_report_key(dev, BTN_STYLUS2, data[1] & 4);
+ input_report_key(dev, BTN_TOUCH, t > 10);
+ }
+
+ /* airbrush second packet */
+ if ((data[1] & 0xbc) == 0xb4)
+ {
+ input_report_abs(dev, ABS_WHEEL,
+ ((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
+ input_report_abs(dev, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+ }
+
+ /* 4D mouse, 2D mouse, or Lens cursor packets */
+ if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0)
+ {
+ /* Rotation packet */
+ if (data[1] & 0x02)
+ {
+ input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ?
+ ((__u32)data[6] << 2) | ((data[7] >> 6) & 3):
+ (-(((__u32)data[6] << 2) | ((data[7] >> 6) & 3))) - 1);
+ }
+
+ /* 4D mouse packets */
+ else if ((data[1] & 0x10) == 0)
+ {
+ input_report_key(dev, BTN_LEFT, data[8] & 0x01);
+ input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
+ input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
+ input_report_key(dev, BTN_SIDE, data[8] & 0x20);
+ input_report_key(dev, BTN_EXTRA, data[8] & 0x10);
+ /* JEJ - throttle is positive when rolled backwards */
+ input_report_abs(dev, ABS_THROTTLE, - ((data[8] & 0x08) ?
+ ((__u32)data[6] << 2) | ((data[7] >> 6) & 3) :
+ -((__u32)data[6] << 2) | ((data[7] >> 6) & 3)));
+ }
+
+ /* 2D mouse packets */
+ else if (wacom->tool[idx] == BTN_TOOL_MOUSE)
+ {
+ /* JEJ - validated with 2D Intuos2 mouse */
+ input_report_key(dev, BTN_LEFT, data[8] & 0x04);
+ input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
+ input_report_key(dev, BTN_RIGHT, data[8] & 0x10);
+ /* JEJ - mouse wheel is positive when rolled backwards */
+ input_report_rel(dev, REL_WHEEL, - (((__u32)(data[8] & 0x01)) -
+ ((__u32)((data[8] & 0x02) >> 1))));
+ }
+
+ /* lens cursor packets */
+ else
+ {
+ input_report_key(dev, BTN_LEFT, data[8] & 0x01);
+ input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
+ input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
+ input_report_key(dev, BTN_SIDE, data[8] & 0x10);
+ input_report_key(dev, BTN_EXTRA, data[8] & 0x08);
+ }
+ }
+
+ input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+}
+
+#define WACOM_GRAPHIRE_BITS (BIT(EV_REL))
+#define WACOM_GRAPHIRE_REL (BIT(REL_WHEEL))
+#define WACOM_INTUOS_TOOLS (BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS))
+#define WACOM_INTUOS_BUTTONS (BIT(BTN_SIDE) | BIT(BTN_EXTRA))
+#define WACOM_INTUOS_BITS (BIT(EV_REL))
+#define WACOM_INTUOS_REL (BIT(REL_WHEEL))
+#define WACOM_INTUOS_ABS (BIT(ABS_TILT_X) | BIT(ABS_TILT_Y) | BIT(ABS_RZ) | BIT(ABS_THROTTLE))
+
+struct wacom_features wacom_features[] = {
+
+ /* PenPartner */
+ /* 0 */ { "Wacom Penpartner", 7, 5040, 3780, 255, 32,
+ wacom_penpartner_irq, 0, 0, 0, 0 },
+
+ /* Graphire */
+ /* 1 */ { "Wacom Graphire", 8, 10206, 7422, 511, 32,
+ wacom_graphire_irq, WACOM_GRAPHIRE_BITS, 0, BIT(REL_WHEEL), 0 },
+ /* 2 */ { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32,
+ wacom_graphire_irq, WACOM_GRAPHIRE_BITS, 0, BIT(REL_WHEEL), 0 },
+ /* 3 */ { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32,
+ wacom_graphire_irq, WACOM_GRAPHIRE_BITS, 0, BIT(REL_WHEEL), 0 },
+
+ /* Intuos */
+ /* 4 */ { "Wacom Intuos 4x5", 10, 12700, 10360, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* JEJ - confirmed X and Y range from test tablet */
+ /* 5 */ { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* 6 */ { "Wacom Intuos 9x12", 10, 30670, 24130, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* 7 */ { "Wacom Intuos 12x12", 10, 30670, 31600, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* 8 */ { "Wacom Intuos 12x18", 10, 45860, 31600, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+
+ /* PL - Cintiq */
+ /* 9 */ { "Wacom PL400", 8, 5408, 4056, 255, 32,
+ wacom_pl_irq, 0, 0, 0, 0 },
+ /* 10 */ { "Wacom PL500", 8, 6144, 4608, 255, 32,
+ wacom_pl_irq, 0, 0, 0, 0 },
+ /* 11 */ { "Wacom PL600", 8, 6126, 4604, 255, 32,
+ wacom_pl_irq, 0, 0, 0, 0 },
+ /* 12 */ { "Wacom PL600SX", 8, 6260, 5016, 255, 32,
+ wacom_pl_irq, 0, 0, 0, 0 },
+ /* 13 */ { "Wacom PL550", 8, 6144, 4608, 511, 32,
+ wacom_pl_irq, 0, 0, 0, 0 },
+ /* 14 */ { "Wacom PL800", 8, 7220, 5780, 511, 32,
+ wacom_pl_irq, 0, 0, 0, 0 },
+
+ /* Intuos2 */
+ /* 15 */ { "Wacom Intuos2 4x5", 10, 12700, 10360, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* JEJ - confirmed X and Y range from R.T. and J.S. tablets */
+ /* 16 */ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* JEJ - values from serial 9x12 */
+ /* 17 */ { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* JEJ - confirmed X and Y range from tablet */
+ /* 18 */ { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+ /* 19 */ { "Wacom Intuos2 12x18", 10, 45860, 31600, 1023, 15,
+ wacom_intuos_irq, WACOM_INTUOS_BITS, WACOM_INTUOS_ABS,
+ WACOM_INTUOS_REL, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS },
+
+ /* Volito - (Graphire2 4x5 no mouse wheel) */
+ /* 20 */ { "Wacom Volito", 8, 10206, 7422, 511, 32,
+ wacom_graphire_irq, WACOM_GRAPHIRE_BITS, 0, 0, 0 },
+
+ { NULL , 0 }
+};
+
+struct usb_device_id wacom_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00), driver_info: 0 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10), driver_info: 1 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11), driver_info: 2 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12), driver_info: 3 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20), driver_info: 4 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21), driver_info: 5 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22), driver_info: 6 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23), driver_info: 7 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24), driver_info: 8 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30), driver_info: 9 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31), driver_info: 10 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32), driver_info: 11 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33), driver_info: 12 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34), driver_info: 13 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35), driver_info: 14 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41), driver_info: 15 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42), driver_info: 16 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43), driver_info: 17 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44), driver_info: 18 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45), driver_info: 19 },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60), driver_info: 20 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, wacom_ids);
+
+static int wacom_open(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ if (wacom->open++)
+ return 0;
+
+ wacom->irq.dev = wacom->usbdev;
+ if (usb_submit_urb(&wacom->irq))
+ return -EIO;
+
+ return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ if (!--wacom->open)
+ usb_unlink_urb(&wacom->irq);
+}
+
+static void *wacom_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *endpoint;
+ struct wacom *wacom;
+ char rep_data[2] = {0x02, 0x02};
+
+ if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL;
+ memset(wacom, 0, sizeof(struct wacom));
+
+ wacom->features = wacom_features + id->driver_info;
+
+ wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC) |
+ wacom->features->evbit;
+ wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) |
+ BIT(ABS_DISTANCE) | BIT(ABS_WHEEL) | wacom->features->absbit;
+ wacom->dev.relbit[0] |= wacom->features->relbit;
+ wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
+ BIT(BTN_MIDDLE) | wacom->features->btnbit;
+ wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) |
+ BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) |
+ BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) |
+ wacom->features->digibit;
+ wacom->dev.mscbit[0] |= BIT(MSC_SERIAL);
+
+ #ifdef __JEJ_DEBUG
+ printk(KERN_INFO "Reporting max %d, %d\n",
+ wacom->features->x_max, wacom->features->y_max);
+ #endif
+
+ wacom->dev.absmax[ABS_X] = wacom->features->x_max;
+ wacom->dev.absmax[ABS_Y] = wacom->features->y_max;
+ wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max;
+ wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max;
+ wacom->dev.absmax[ABS_TILT_X] = 127;
+ wacom->dev.absmax[ABS_TILT_Y] = 127;
+ wacom->dev.absmax[ABS_WHEEL] = 1023;
+
+ wacom->dev.absmin[ABS_RZ] = -900;
+ wacom->dev.absmax[ABS_RZ] = 899;
+ wacom->dev.absmin[ABS_THROTTLE] = -1023;
+ wacom->dev.absmax[ABS_THROTTLE] = 1023;
+
+ wacom->dev.absfuzz[ABS_X] = 4;
+ wacom->dev.absfuzz[ABS_Y] = 4;
+
+ wacom->dev.private = wacom;
+ wacom->dev.open = wacom_open;
+ wacom->dev.close = wacom_close;
+
+ wacom->dev.name = wacom->features->name;
+ wacom->dev.idbus = BUS_USB;
+ wacom->dev.idvendor = dev->descriptor.idVendor;
+ wacom->dev.idproduct = dev->descriptor.idProduct;
+ wacom->dev.idversion = dev->descriptor.bcdDevice;
+ wacom->usbdev = dev;
+
+ endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+
+ usb_set_idle(dev, dev->config[0].interface[ifnum].altsetting[0].bInterfaceNumber, 0, 0);
+
+ FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev,
+ endpoint->bEndpointAddress), wacom->data, wacom->features->pktlen,
+ wacom->features->irq, wacom, endpoint->bInterval);
+
+ input_register_device(&wacom->dev);
+
+ #ifdef __JEJ_DEBUG
+ printk(KERN_INFO __FILE__ ": Setting tablet report for tablet data\n");
+ #endif
+
+ /* ask the tablet to report tablet data */
+ usb_set_report(dev, ifnum, 3, 2, rep_data, 2);
+ usb_set_report(dev, ifnum, 3, 5, rep_data, 0);
+ usb_set_report(dev, ifnum, 3, 6, rep_data, 0);
+
+ printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
+ wacom->dev.number, wacom->features->name, dev->bus->busnum,
+ dev->devnum, ifnum);
+
+ return wacom;
+}
+
+static void wacom_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct wacom *wacom = ptr;
+ if (wacom) {
+ usb_unlink_urb(&wacom->irq);
+ input_unregister_device(&wacom->dev);
+ kfree(wacom);
+ }
+}
+
+static struct usb_driver wacom_driver = {
+ name: "wacom",
+ probe: wacom_probe,
+ disconnect: wacom_disconnect,
+ id_table: wacom_ids,
+};
+
+static int __init wacom_init(void)
+{
+ usb_register(&wacom_driver);
+ info(DRIVER_VERSION " " DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+ return 0;
+}
+
+static void __exit wacom_exit(void)
+{
+ usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff --git a/src/wacscrn.c b/src/wacscrn.c
new file mode 100644
index 0000000..608be65
--- /dev/null
+++ b/src/wacscrn.c
@@ -0,0 +1,39 @@
+/*****************************************************************************
+** wacscrn.c
+**
+** Copyright (C) 2002 - John E. Joganic
+**
+** 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**
+****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ncurses.h>
+
+/****************************************************************************/
+
+void wacscrn_init(void)
+ { initscr(); }
+void wacscrn_term(void)
+ { endwin(); }
+void wacscrn_output(int y, int x, const char* pszText)
+ { mvaddstr(y,x,pszText); }
+void wacscrn_standout(void)
+ { attron(A_STANDOUT); }
+void wacscrn_normal(void)
+ { attrset(A_NORMAL); }
+void wacscrn_refresh(void)
+ { refresh(); }
diff --git a/src/wacscrn.h b/src/wacscrn.h
new file mode 100644
index 0000000..ba33f01
--- /dev/null
+++ b/src/wacscrn.h
@@ -0,0 +1,32 @@
+/*****************************************************************************
+** wacscrn.h
+**
+** Copyright (C) 2002 - John E. Joganic
+**
+** 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**
+****************************************************************************/
+
+#ifndef __WACPACK_WACSCRN_H
+#define __WACPACK_WACSCRN_H
+
+void wacscrn_init(void);
+void wacscrn_term(void);
+void wacscrn_output(int y, int x, const char* pszText);
+void wacscrn_standout(void);
+void wacscrn_normal(void);
+void wacscrn_refresh(void);
+
+#endif /* __WACPACK_WACSCRN_H */
diff --git a/src/wacserial.c b/src/wacserial.c
new file mode 100644
index 0000000..7d0e5b7
--- /dev/null
+++ b/src/wacserial.c
@@ -0,0 +1,966 @@
+/*****************************************************************************
+** wacserial.c
+**
+** Copyright (C) 2002 - John E. Joganic
+**
+** 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**
+****************************************************************************/
+
+#include "wacserial.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+
+/*****************************************************************************
+** Structures
+*****************************************************************************/
+
+typedef struct
+{
+ const char* pszID;
+ unsigned int uDeviceType;
+ const char* pszName;
+ unsigned int uPacketLength;
+ int nProtocol;
+ int nCaps;
+} MODEL_INFO;
+
+/*****************************************************************************
+** Tablet object
+*****************************************************************************/
+
+typedef struct _TABLET TABLET;
+
+typedef int (*PARSEFUNC)(TABLET* pTablet, const unsigned char* puchData,
+ unsigned int uLength, WACOMSTATE* pState);
+
+struct _TABLET
+{
+ int fd;
+ MODEL_INFO* pInfo;
+ unsigned int uPacketLength;
+ int nVerMajor, nVerMinor, nVerRelease;
+ int nCaps;
+ PARSEFUNC pfnParse;
+ int nToolID;
+ WACOMSTATE state, max, min;
+};
+
+static int TabletIdentifyModel(TABLET* pTablet);
+static int TabletInitializeModel(TABLET* pTablet);
+
+static int TabletParseWacomV(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState);
+static int TabletParseWacomIV_1_4(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState);
+static int TabletParseWacomIV_1_3(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState);
+static int TabletParseWacomIV_1_2(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState);
+
+#ifndef BIT
+#undef BIT
+#define BIT(x) (1<<(x))
+#endif
+
+#define WACOMVALID(x) BIT(WACOMFIELD_##x)
+
+#define ARTPADII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
+ WACOMVALID(PRESSURE))
+
+#define DIGITIZERII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
+ WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))
+
+#define INTUOS_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \
+ WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \
+ WACOMVALID(POSITION_Y)|WACOMVALID(ROTATION_Z)|WACOMVALID(DISTANCE)| \
+ WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)| \
+ WACOMVALID(ABSWHEEL)|WACOMVALID(RELWHEEL)|WACOMVALID(THROTTLE))
+
+#define INTUOS2_CAPS INTUOS_CAPS
+
+/*****************************************************************************
+** Globals
+*****************************************************************************/
+
+ static MODEL_INFO xModels[] =
+ {
+ { "KT-0405-R", WACOMDEVICE_ARTPADII, "Wacom ArtPadII 4x5", 7, 4,
+ ARTPADII_CAPS },
+
+ { "UD-0608-R", WACOMDEVICE_DIGITIZERII, "Wacom DigitizerII 6x8", 7, 4,
+ DIGITIZERII_CAPS },
+ { "UD-1212-R", WACOMDEVICE_DIGITIZERII, "Wacom DigitizerII 12x12", 7, 4,
+ DIGITIZERII_CAPS },
+ { "UD-1218-R", WACOMDEVICE_DIGITIZERII, "Wacom DigitizerII 12x18", 7, 4,
+ DIGITIZERII_CAPS },
+ { "UD-1825-R", WACOMDEVICE_DIGITIZERII, "Wacom DigitizerII 18x25", 7, 4,
+ DIGITIZERII_CAPS },
+ /*
+ { "CT-0405-R", WACOMDEVICE_PENPARTNER, "Wacom PenPartner", 7, 4 },
+ { "ET-0405-R", WACOMDEVICE_GRAPHIRE, "Wacom Graphire", 7, 4 },
+
+ { "?D-0405-R", WACOMDEVICE_GRAPHIRE2, "Wacom Graphire2 4x5", 7, 4 },
+ { "?D-0507-R", WACOMDEVICE_GRAPHIRE2, "Wacom Graphire2 5x7", 7, 4 },
+ */
+
+ { "GD-0405-R", WACOMDEVICE_INTUOS, "Wacom Intuos 4x5", 9, 5,
+ INTUOS_CAPS },
+ { "GD-0608-R", WACOMDEVICE_INTUOS, "Wacom Intuos 6x8", 9, 5,
+ INTUOS_CAPS },
+ { "GD-0912-R", WACOMDEVICE_INTUOS, "Wacom Intuos 9x12", 9, 5,
+ INTUOS_CAPS },
+ { "GD-1212-R", WACOMDEVICE_INTUOS, "Wacom Intuos 12x12", 9, 5,
+ INTUOS_CAPS },
+ { "GD-1218-R", WACOMDEVICE_INTUOS, "Wacom Intuos 12x18", 9, 5,
+ INTUOS_CAPS },
+
+ { "XD-0405-R", WACOMDEVICE_INTUOS2, "Wacom Intuos2 4x5", 9, 5,
+ INTUOS2_CAPS },
+ { "XD-0608-R", WACOMDEVICE_INTUOS2, "Wacom Intuos2 6x8", 9, 5,
+ INTUOS2_CAPS },
+ { "XD-0912-R", WACOMDEVICE_INTUOS2, "Wacom Intuos2 9x12", 9, 5,
+ INTUOS2_CAPS },
+ { "XD-1212-R", WACOMDEVICE_INTUOS2, "Wacom Intuos2 12x12", 9, 5,
+ INTUOS2_CAPS },
+ { "XD-1218-R", WACOMDEVICE_INTUOS2, "Wacom Intuos2 12x18", 9, 5,
+ INTUOS2_CAPS },
+
+ { NULL }
+ };
+
+/*****************************************************************************
+** Static Prototypes
+*****************************************************************************/
+
+static int WacomSendReset(int fd);
+static int WacomSendStop(int fd);
+static int WacomSendStart(int fd);
+
+static int WacomSend(int fd, const char* pszData);
+static int WacomSendRaw(int fd, const unsigned char* puchData,
+ unsigned int uSize);
+static int WacomFlush(int fd);
+static int WacomSendRequest(int fd, const char* pszRequest, char* pchResponse,
+ unsigned int uSize);
+
+/*****************************************************************************
+** Public Functions
+*****************************************************************************/
+
+WACOMTABLET WacomOpenSerial(const char* pszDevice)
+{
+ int fd;
+ struct termios tios;
+ TABLET* pTablet = NULL;
+
+ /* open device for read/write access */
+ fd = open(pszDevice,O_RDWR);
+ if (fd < 0)
+ { perror("open"); return NULL; }
+
+ /* configure tty */
+ if (isatty(fd))
+ {
+ /* set up default port parameters */
+ if (tcgetattr (fd, &tios))
+ { perror("tcgetattr"); close(fd); return NULL; }
+
+ tios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ tios.c_oflag &= ~OPOST;
+ tios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+ tios.c_cflag &= ~(CSIZE|PARENB);
+ tios.c_cflag |= CS8|CLOCAL;
+
+ tios.c_cflag &= ~(CSTOPB); /* 1 stop bit */
+
+ tios.c_cflag &= ~(CSIZE); /* 8 data bits */
+ tios.c_cflag |= CS8;
+
+ tios.c_cflag &= ~(PARENB); /* no parity */
+
+ tios.c_iflag |= IXOFF; /* flow control XOff */
+
+#if 0
+ {
+ int nValue;
+ /* clear DTR */
+ nValue = TIOCCDTR;
+ if (ioctl(fd, TIOCMBIC, &nValue))
+ { perror("clear dtr"); close(fd); return NULL; }
+
+ /* clear RTS */
+ nValue = TIOCM_RTS;
+ if (ioctl(fd, TIOCMBIC, &nValue))
+ { perror("clear rts"); close(fd); return NULL; }
+ }
+#endif
+
+ tios.c_cc[VMIN] = 1; /* vmin value */
+ tios.c_cc[VTIME] = 0; /* vtime value */
+
+ if (tcsetattr (fd, TCSANOW, &tios))
+ { perror("tcsetattr"); close(fd); return NULL; }
+
+ /* start high and work down */
+ cfsetispeed(&tios, B38400);
+ cfsetospeed(&tios, B38400);
+ if (WacomSendReset(fd)) { close(fd); return NULL; }
+ cfsetispeed(&tios, B19200);
+ cfsetospeed(&tios, B19200);
+ if (WacomSendReset(fd)) { close(fd); return NULL; }
+ cfsetispeed(&tios, B9600);
+ cfsetospeed(&tios, B9600);
+ if (WacomSendReset(fd)) { close(fd); return NULL; }
+ }
+ else /* not tty */
+ {
+ if (WacomSendReset(fd)) { close(fd); return NULL; }
+ }
+
+ /* Send stop */
+ if (WacomSendStop(fd) || WacomFlush(fd))
+ { perror("stop"); close(fd); return NULL; }
+
+ /* Allocate tablet */
+ pTablet = (TABLET*)malloc(sizeof(TABLET));
+ memset(pTablet,0,sizeof(*pTablet));
+ pTablet->fd = fd;
+
+ /* Identify and initialize the model */
+ if (TabletIdentifyModel(pTablet))
+ { perror("identify"); close(fd); free(pTablet); return NULL; }
+ if (TabletInitializeModel(pTablet))
+ { perror("init_model"); close(fd); free(pTablet); return NULL; }
+
+ /* Send start */
+ WacomSendStart(fd);
+
+ return (WACOMTABLET)pTablet;
+}
+
+void WacomCloseSerial(WACOMTABLET hTablet)
+{
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) return;
+
+ close(pTablet->fd);
+ free(pTablet);
+}
+
+WACOMMODEL WacomGetModel(WACOMTABLET hTablet)
+{
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 0; }
+ return pTablet->pInfo->uDeviceType | WACOMCLASS_SERIAL;
+}
+
+const char* WacomGetModelName(WACOMTABLET hTablet)
+{
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 0; }
+ return pTablet->pInfo->pszName;
+}
+
+int WacomGetRomVersion(WACOMTABLET hTablet, int* pnMajor, int* pnMinor,
+ int* pnRelease)
+{
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 0; }
+ if (!pnMajor) { errno=EINVAL; return 1; }
+ *pnMajor = pTablet->nVerMajor;
+ if (pnMinor) *pnMinor = pTablet->nVerMinor;
+ if (pnRelease) *pnRelease = pTablet->nVerRelease;
+ return 0;
+}
+
+int WacomReadRaw(WACOMTABLET hTablet, unsigned char* puchData,
+ unsigned int uSize)
+{
+ int nXfer;
+ unsigned int uCnt, uPacketLength;
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 0; }
+ uPacketLength = pTablet->uPacketLength;
+
+ /* check size of buffer */
+ if (uSize < uPacketLength) { errno=EINVAL; return 0; }
+
+ for (uCnt=0; uCnt<uPacketLength; uCnt+=nXfer)
+ {
+ nXfer = read(pTablet->fd,puchData+uCnt,uPacketLength-uCnt);
+ if (nXfer <= 0) return nXfer;
+ }
+
+ return (signed)uCnt;
+}
+
+int WacomGetCapabilities(WACOMTABLET hTablet)
+{
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 0; }
+ return pTablet->pInfo->nCaps;
+}
+
+int WacomGetRanges(WACOMTABLET hTablet, WACOMSTATE* pMin, WACOMSTATE* pMax)
+{
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 0; }
+
+ *pMin = pTablet->min;
+ *pMax = pTablet->max;
+ return 0;
+}
+
+
+int WacomParseData(WACOMTABLET hTablet, const unsigned char* puchData,
+ unsigned int uLength, WACOMSTATE* pState)
+{
+ int i;
+ TABLET* pTablet = (TABLET*)hTablet;
+ if (!pTablet) { errno=EBADF; return 1; }
+
+ /* check synchronization */
+ if (!puchData[0] & 0x80) { errno=EINVAL; return 1; }
+ for (i=1; i<uLength; ++i)
+ {
+ if (puchData[i] & 0x80)
+ { errno=EINVAL; return 1; }
+ }
+
+ /* dispatch to parser */
+ if (pTablet->pfnParse)
+ return (*pTablet->pfnParse)(pTablet,puchData,uLength,pState);
+
+ errno = EINVAL;
+ return 1;
+}
+
+/*****************************************************************************
+** Tablet Functions
+*****************************************************************************/
+
+static int TabletIdentifyModel(TABLET* pTablet)
+{
+ char* pszPos;
+ MODEL_INFO* pInfo;
+ char chResp[64];
+
+ if (WacomSendRequest(pTablet->fd,"~#\r",chResp,sizeof(chResp)))
+ return 1;
+
+ /* look through model table for information */
+ for (pInfo=xModels; pInfo->pszID; ++pInfo)
+ {
+ if (strncmp(chResp,pInfo->pszID,strlen(pInfo->pszID)) == 0)
+ {
+ pTablet->pInfo = pInfo;
+ pTablet->uPacketLength = pInfo->uPacketLength;
+
+ /* get version number */
+ pszPos = chResp;
+ while (*pszPos) ++pszPos;
+ while ((pszPos > chResp) && (pszPos[-1] != 'V')) --pszPos;
+ if (sscanf(pszPos,"%d.%d-%d",&pTablet->nVerMajor,
+ &pTablet->nVerMinor,&pTablet->nVerRelease) != 3)
+ {
+ pTablet->nVerRelease = 0;
+ if (sscanf(pszPos,"%d.%d",&pTablet->nVerMajor,
+ &pTablet->nVerMinor) != 2)
+ {
+ errno = EINVAL;
+ fprintf(stderr,"bad version number: %s\n",pszPos);
+ return 1;
+ }
+ }
+ return 0;
+ }
+ }
+
+ fprintf(stderr,"UNIDENTIFIED TABLET: %s\n",chResp);
+ return 1;
+}
+
+static int TabletInitializeModel(TABLET* pTablet)
+{
+ char chResp[32];
+
+ /* Request tablet dimensions */
+ if (WacomSendRequest(pTablet->fd,"~C\r",chResp,sizeof(chResp)))
+ return 1;
+ if (sscanf(chResp,"%d,%d",&pTablet->max.nPosX,&pTablet->max.nPosY) != 2)
+ { errno=EINVAL; perror("bad dim response"); return 1; }
+
+ /* tablet specific initialization */
+ switch (pTablet->pInfo->uDeviceType)
+ {
+ case WACOMDEVICE_PENPARTNER:
+ /* pressure mode */
+ WacomSend(pTablet->fd, "PH1\r");
+ break;
+
+ case WACOMDEVICE_INTUOS:
+ /* multi-mode, max-rate */
+ WacomSend(pTablet->fd, "MT1\rID1\rIT0\r");
+ }
+
+ if (pTablet->pInfo->nProtocol == 4)
+ {
+ /* multi-mode (MU), upper-origin (OC), all-macro (M0),
+ * no-macro1 (M1), max-rate (IT), no-inc (IN),
+ * stream-mode (SR), Z-filter (ZF) */
+
+/* if (WacomSend(pTablet->fd, "MU1\rOC1\r~M0\r~M1\rIT0\rIN0\rSR\rZF1\r"))
+ return 1;
+ */
+
+ if (pTablet->nVerMajor == 1)
+ {
+ if (pTablet->nVerMinor >= 4)
+ {
+ /* enable tilt mode */
+ if (WacomSend(pTablet->fd,"FM1\r")) return 1;
+
+ pTablet->pfnParse = TabletParseWacomIV_1_4;
+ pTablet->uPacketLength = 9;
+ pTablet->max.nPressure = 255;
+ pTablet->min.nTiltX = -64;
+ pTablet->max.nTiltX = 63;
+ pTablet->min.nTiltY = -64;
+ pTablet->max.nTiltY = 63;
+ }
+ else if (pTablet->nVerMinor == 3)
+ {
+ pTablet->pfnParse = TabletParseWacomIV_1_3;
+ pTablet->max.nPressure = 255;
+ }
+ else if (pTablet->nVerMinor == 2)
+ {
+ pTablet->pfnParse = TabletParseWacomIV_1_2;
+ pTablet->max.nPressure = 255;
+ }
+ else if (pTablet->nVerMinor < 2)
+ {
+ pTablet->pfnParse = TabletParseWacomIV_1_2;
+ pTablet->max.nPressure = 120;
+ }
+ }
+ }
+ else if (pTablet->pInfo->nProtocol == 5)
+ {
+ pTablet->pfnParse = TabletParseWacomV;
+ pTablet->max.nPressure = 1023;
+ pTablet->max.nAbsWheel = 1023;
+ pTablet->min.nRotZ = -899;
+ pTablet->max.nRotZ = 900;
+ pTablet->min.nThrottle = -1023;
+ pTablet->max.nThrottle = 1023;
+ pTablet->min.nTiltX = -64;
+ pTablet->max.nTiltX = 63;
+ pTablet->min.nTiltY = -64;
+ pTablet->max.nTiltY = 63;
+ }
+ else { errno=EINVAL; return 1; }
+
+ return 0;
+}
+
+static int TabletParseWacomV(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState)
+{
+ int x=0, y=0, rot=0, tiltx=0, tilty=0, wheel=0,
+ tool=WACOMTOOLTYPE_NONE, button=0, press=0, throttle=0;
+
+ /* Wacom V
+ * Supports: 1024 pressure, eraser, 2 side-switch, tilt, throttle, wheel
+ * Limitation: no tilt */
+
+ if (uLength != 9) { errno=EINVAL; return 1; }
+
+ /* in */
+ if ((puchData[0] & 0xFC) == 0xC0)
+ {
+ int toolid = (((int)puchData[1]&0x7F) << 5) |
+ (((int)puchData[2]&0x7C) >> 2);
+
+ int serial = ((((int)puchData[2] & 0x03) << 30) |
+ (((int)puchData[3] & 0x7f) << 23) |
+ (((int)puchData[4] & 0x7f) << 16) |
+ (((int)puchData[5] & 0x7f) << 9) |
+ (((int)puchData[6] & 0x7f) << 23) |
+ (((int)puchData[7] & 0x60) >> 5));
+
+ switch (toolid)
+ {
+ case 0x812: /* Intuos2 ink pen XP-110-00A */
+ case 0x012: /* Inking pen */
+ tool = WACOMTOOLTYPE_PENCIL; break;
+
+ case 0x822: /* Intuos Pen GP-300E-01H */
+ case 0x852: /* Intuos2 Grip Pen XP-501E-00A */
+ case 0x842: /* added from Cheng */
+ case 0x022:
+ tool = WACOMTOOLTYPE_PEN; break;
+
+ case 0x832: /* Intuos2 stroke pen XP-120-00A */
+ case 0x032: /* Stroke pen */
+ tool = WACOMTOOLTYPE_BRUSH; break;
+
+ case 0x007: /* 2D Mouse */
+ case 0x09C: /* ?? Mouse */
+ case 0x094: /* 4D Mouse */
+ tool = WACOMTOOLTYPE_MOUSE; break;
+
+ case 0x096: /* Lens cursor */
+ tool = WACOMTOOLTYPE_LENS; break;
+
+ case 0x82a:
+ case 0x85a:
+ case 0x91a:
+ case 0x0fa: /* Eraser */
+ tool = WACOMTOOLTYPE_ERASER; break;
+
+ case 0x112: /* Airbrush */
+ tool = WACOMTOOLTYPE_AIRBRUSH; break;
+
+ default: /* Unknown tool */
+ tool = WACOMTOOLTYPE_PEN; break;
+ }
+
+ pTablet->nToolID = toolid;
+ pTablet->state.nValid = pTablet->nCaps;
+ pTablet->state.nProximity = 1;
+ pTablet->state.nSerial = serial;
+ pTablet->state.nToolType = tool;
+ *pState = pTablet->state;
+ return 0;
+ }
+
+ /* out */
+ if ((puchData[0] & 0xFE) == 0x80)
+ {
+ pTablet->nToolID = 0;
+ memset(&pTablet->state,0,sizeof(pTablet->state));
+ pTablet->state.nValid = pTablet->nCaps;
+ *pState = pTablet->state;
+ return 0;
+ }
+
+ /* pen data */
+ if (((puchData[0] & 0xB8) == 0xA0) || ((puchData[0] & 0xBE) == 0xB4))
+ {
+ x = ((((int)puchData[1] & 0x7f) << 9) |
+ (((int)puchData[2] & 0x7f) << 2) |
+ (((int)puchData[3] & 0x60) >> 5));
+ y = ((((int)puchData[3] & 0x1f) << 11) |
+ (((int)puchData[4] & 0x7f) << 4) |
+ (((int)puchData[5] & 0x78) >> 3));
+ tiltx = (puchData[7] & 0x3F);
+ tilty = (puchData[8] & 0x3F);
+ if (puchData[7] & 0x40) tiltx -= 0x40;
+ if (puchData[8] & 0x40) tilty -= 0x40;
+
+ /* pen packet */
+ if ((puchData[0] & 0xB8) == 0xA0)
+ {
+ press = ((((int)puchData[5] & 0x07) << 7) | ((int)puchData[6] & 0x7f));
+ button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
+ button |= (puchData[0] & 0x02) ? BIT(WACOMBUTTON_STYLUS) : 0;
+ button |= (puchData[0] & 0x04) ? BIT(WACOMBUTTON_STYLUS2) : 0;
+ }
+
+ /* 2nd airbrush packet */
+ else
+ {
+ wheel = ((((int)puchData[5] & 0x07) << 7) |
+ ((int)puchData[6] & 0x7f));
+ }
+
+ pTablet->state.nValid = pTablet->nCaps;
+ pTablet->state.nPosX = x;
+ pTablet->state.nPosY = y;
+ pTablet->state.nTiltX = tiltx;
+ pTablet->state.nTiltY = tilty;
+ pTablet->state.nPressure = press;
+ pTablet->state.nButtons = button;
+ pTablet->state.nAbsWheel = wheel;
+ *pState = pTablet->state;
+ return 0;
+ }
+
+ /* mouse packet */
+ if (((puchData[0] & 0xBE) == 0xA8) || ((puchData[0] & 0xBE) == 0xB0))
+ {
+ x = ((((int)puchData[1] & 0x7f) << 9) |
+ (((int)puchData[2] & 0x7f) << 2) |
+ (((int)puchData[3] & 0x60) >> 5));
+ y = ((((int)puchData[3] & 0x1f) << 11) |
+ (((int)puchData[4] & 0x7f) << 4) |
+ (((int)puchData[5] & 0x78) >> 3));
+ throttle = ((((int)puchData[5] & 0x07) << 7) | (puchData[6] & 0x7f));
+ if (puchData[8] & 0x08) throttle = -throttle;
+
+ /* 4D mouse */
+ if (pTablet->nToolID == 0x094)
+ {
+ button = (((puchData[8] & 0x70) >> 1) | (puchData[8] & 0x07));
+ }
+ /* lens cursor */
+ else if (pTablet->nToolID == 0x096)
+ {
+ button = puchData[8] & 0x1F;
+ }
+ /* 2D mouse */
+ else
+ {
+ button = (puchData[8] & 0x1C) >> 2;
+ wheel = - (puchData[8] & 1) + ((puchData[8] & 2) >> 1);
+ }
+
+ pTablet->state.nValid = pTablet->nCaps;
+ pTablet->state.nPosX = x;
+ pTablet->state.nPosY = y;
+ pTablet->state.nRelWheel = wheel;
+ pTablet->state.nThrottle = throttle;
+ pTablet->state.nButtons &= ~(BIT(WACOMBUTTON_LEFT) |
+ BIT(WACOMBUTTON_RIGHT) | BIT(WACOMBUTTON_MIDDLE) |
+ BIT(WACOMBUTTON_EXTRA) | BIT(WACOMBUTTON_SIDE));
+ if (button & 1) pTablet->state.nButtons |= BIT(WACOMBUTTON_LEFT);
+ if (button & 2) pTablet->state.nButtons |= BIT(WACOMBUTTON_MIDDLE);
+ if (button & 4) pTablet->state.nButtons |= BIT(WACOMBUTTON_RIGHT);
+ if (button & 8) pTablet->state.nButtons |= BIT(WACOMBUTTON_EXTRA);
+ if (button & 16) pTablet->state.nButtons |= BIT(WACOMBUTTON_SIDE);
+
+ *pState = pTablet->state;
+ return 0;
+ }
+
+ /* 2nd 4D mouse packet */
+ if ((puchData[0] & 0xBE) == 0xAA)
+ {
+ x = ((((int)puchData[1] & 0x7f) << 9) |
+ (((int)puchData[2] & 0x7f) << 2) |
+ (((int)puchData[3] & 0x60) >> 5));
+ y = ((((int)puchData[3] & 0x1f) << 11) |
+ (((int)puchData[4] & 0x7f) << 4) |
+ (((int)puchData[5] & 0x78) >> 3));
+ rot = ((((int)puchData[6] & 0x0f) << 7) |
+ ((int)puchData[7] & 0x7f));
+
+ /* FIX ROT */
+ if (rot < 900) rot = -rot;
+ else rot = 1800 - rot;
+
+ pTablet->state.nPosX = x;
+ pTablet->state.nPosY = y;
+ pTablet->state.nRotZ = rot;
+ *pState = pTablet->state;
+ return 0;
+ }
+
+ errno = EINVAL;
+ return 1;
+}
+
+static int TabletParseWacomIV_1_4(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState)
+{
+ /* Wacom IV, Rom 1.4
+ * Supports: 256 pressure, eraser, 2 side-switch, tilt */
+
+ if ((uLength != 7) && (uLength != 9)) { errno=EINVAL; return 1; }
+
+ if (TabletParseWacomIV_1_3(pTablet,puchData,7,pState))
+ return 1;
+
+ /* tilt mode */
+ if (uLength == 9)
+ {
+ pTablet->state.nValid |= WACOMVALID(TILT_X) | WACOMVALID(TILT_Y);
+ pTablet->state.nTiltX = puchData[7] & 0x3F;
+ pTablet->state.nTiltY = puchData[8] & 0x3F;
+ if (puchData[7] & 0x40) pTablet->state.nTiltX -= 64;
+ if (puchData[8] & 0x40) pTablet->state.nTiltY -= 64;
+
+ pState->nValid = pTablet->state.nValid;
+ pState->nTiltX = pTablet->state.nTiltX;
+ pState->nTiltY = pTablet->state.nTiltY;
+ }
+
+ return 0;
+}
+
+static int TabletParseWacomIV_1_3(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState)
+{
+ int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE,
+ button=0, press=0, stylus, eraser;
+
+ /* Wacom IV, Rom 1.3 (ArtPadII)
+ * Supports: 256 pressure, eraser, 2 side-switch
+ * Limitation: no tilt */
+
+ if (uLength != 7) { errno=EINVAL; return 1; }
+
+ prox = puchData[0] & 0x40 ? 1 : 0;
+ if (prox)
+ {
+ stylus = puchData[0] & 0x20 ? 1 : 0;
+ press = (puchData[6] & 0x3F) << 1 | ((puchData[3] & 0x4) >> 2);
+ press |= (puchData[6] & 0x40) ? 0 : 0x80;
+ eraser = (puchData[3] & 0x20) ? 1 : 0;
+
+ if (stylus)
+ {
+ /* if entering proximity, choose eraser or stylus2 for bit */
+ if (pTablet->state.nProximity == 0)
+ {
+ if (eraser) tool = WACOMTOOLTYPE_ERASER;
+ else tool = WACOMTOOLTYPE_PEN;
+ }
+
+ /* otherwise, keep the last tool */
+ else tool = pTablet->state.nToolType;
+
+ button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
+
+ /* pen has 2 side-switch, eraser has none */
+ if (tool == WACOMTOOLTYPE_PEN)
+ {
+ button |= (puchData[3] & 0x10) ?
+ BIT(WACOMBUTTON_STYLUS) : 0;
+ button |= (eraser) ? BIT(WACOMBUTTON_STYLUS2) : 0;
+ }
+ }
+ else
+ {
+ tool = WACOMTOOLTYPE_MOUSE;
+ button = (puchData[3] & 0x78) >> 3; /* not tested */
+ }
+
+ x = puchData[2] | ((int)puchData[1] << 7) |
+ (((int)puchData[0] & 0x3) << 14);
+ y = puchData[5] | ((int)puchData[4] << 7) |
+ (((int)puchData[3] & 0x3) << 14);
+ }
+
+ /* set valid fields */
+ pTablet->state.nValid = WACOMVALID(PROXIMITY) |
+ WACOMVALID(TOOLTYPE) | WACOMVALID(POSITION_X) |
+ WACOMVALID(POSITION_Y) | WACOMVALID(PRESSURE) |
+ WACOMVALID(BUTTONS);
+
+ pTablet->state.nProximity = prox;
+ pTablet->state.nToolType = tool;
+ pTablet->state.nPosX = x;
+ pTablet->state.nPosY = y;
+ pTablet->state.nPressure = press;
+ pTablet->state.nButtons = button;
+
+ *pState = pTablet->state;
+ return 0;
+}
+
+
+static int TabletParseWacomIV_1_2(TABLET* pTablet,
+ const unsigned char* puchData, unsigned int uLength,
+ WACOMSTATE* pState)
+{
+ int x=0, y=0, prox=0, tool=0, button=WACOMTOOLTYPE_NONE,
+ press=0, stylus;
+
+ /* Wacom IV, Rom 1.2, 1.1, and 1.0
+ * Supports: 256 pressure (120 for 1.1 and 1.0), multi-mode
+ * Limitation: no stylus2, no tilt, no eraser */
+
+ if (uLength != 7) { errno=EINVAL; return 1; }
+
+ prox = puchData[0] & 0x40 ? 1 : 0;
+ if (prox)
+ {
+ stylus = puchData[0] & 0x20 ? 1 : 0;
+ if (pTablet->nVerMinor == 2)
+ press = (puchData[6] & 0x3F) << 1 | ((puchData[3] & 0x4) >> 2) |
+ (puchData[6] & 0x40) ? 0 : 0x80;
+ else
+ press = (puchData[6] & 0x3F) + (puchData[6] & 0x40) ? 0 : 64;
+
+ if (stylus)
+ {
+ tool = WACOMTOOLTYPE_PEN;
+ button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
+ button |= (puchData[3] & 0x10) ? BIT(WACOMBUTTON_STYLUS) : 0;
+ }
+ else
+ {
+ tool = WACOMTOOLTYPE_MOUSE;
+ button = (puchData[3] & 0x78) >> 3; /* not tested */
+ }
+
+ x = puchData[2] | ((int)puchData[1] << 7) |
+ (((int)puchData[0] & 0x3) << 14);
+ y = puchData[5] | ((int)puchData[4] << 7) |
+ (((int)puchData[3] & 0x3) << 14);
+ }
+
+ /* set valid fields */
+ pTablet->state.nValid = WACOMVALID(PROXIMITY) |
+ WACOMVALID(TOOLTYPE) | WACOMVALID(POSITION_X) |
+ WACOMVALID(POSITION_Y) | WACOMVALID(PRESSURE) |
+ WACOMVALID(BUTTONS);
+
+ pTablet->state.nProximity = prox;
+ pTablet->state.nToolType = tool;
+ pTablet->state.nPosX = x;
+ pTablet->state.nPosY = y;
+ pTablet->state.nPressure = press;
+ pTablet->state.nButtons = button;
+
+ *pState = pTablet->state;
+ return 0;
+}
+
+
+/*****************************************************************************
+** Internal Functions
+*****************************************************************************/
+
+static int WacomSendReset(int fd)
+{
+ /* reset to Wacom II-S command set, and factory defaults */
+ if (WacomSend(fd,"\r$")) return 1;
+ usleep(250000); /* 250 milliseconds */
+
+ /* reset tablet to Wacom IV command set */
+ if (WacomSend(fd,"\r#")) return 1;
+ usleep(75000); /* 75 milliseconds */
+
+ return 0;
+}
+
+static int WacomSendStop(int fd)
+{
+ if (WacomSend(fd,"\rSP\r")) return 1;
+ usleep(100000);
+ return 0;
+}
+
+static int WacomSendStart(int fd)
+{
+ return WacomSend(fd,"ST\r");
+}
+
+static int WacomSend(int fd, const char* pszMsg)
+{
+ return WacomSendRaw(fd,pszMsg,strlen(pszMsg));
+}
+
+static int WacomSendRaw(int fd, const unsigned char* puchData,
+ unsigned int uSize)
+{
+ int nXfer;
+ unsigned int uCnt=0;
+
+ while (uCnt < uSize)
+ {
+ nXfer = write(fd,puchData+uCnt,uSize-uCnt);
+ if (!nXfer) { perror("sendraw confused"); return 1; }
+ if (nXfer < 0) { perror("sendraw bad"); return 1; }
+ uCnt += nXfer;
+ }
+
+ return 0;
+}
+
+static int WacomFlush(int fd)
+{
+ char ch[16];
+ fd_set fdsRead;
+ struct timeval timeout;
+
+ if (tcflush(fd, TCIFLUSH) == 0)
+ return 0;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ while (1)
+ {
+ FD_ZERO(&fdsRead);
+ FD_SET(fd, &fdsRead);
+ if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
+ break;
+ read(fd,&ch,sizeof(ch));
+ }
+
+ return 0;
+}
+
+static int WacomSendRequest(int fd, const char* pszRequest, char* pchResponse,
+ unsigned int uSize)
+{
+ int nXfer;
+ unsigned int uLen, uCnt;
+
+ uLen = strlen(pszRequest);
+ if (WacomSendRaw(fd,pszRequest,uLen)) return 1;
+ --uLen;
+
+ if (uSize < uLen) { errno=EINVAL; perror("bad size"); return 1; }
+
+ /* read response header */
+ for (uCnt=0; uCnt<uLen; uCnt+=nXfer)
+ {
+ nXfer = read(fd,pchResponse+uCnt,uLen-uCnt);
+ if (nXfer <= 0) { perror("trunc response header"); return 1; }
+ }
+
+ /* check the header */
+ if (strncmp(pszRequest,pchResponse,uLen) != 0)
+ { perror("bad response header"); return 1; }
+
+ /* get the rest of the response */
+ for (uCnt=0; uCnt<uSize; ++uCnt)
+ {
+ nXfer = read(fd,pchResponse+uCnt,1);
+ if (nXfer <= 0) { perror("bad response read"); return 1; }
+
+ /* stop on CR */
+ if (pchResponse[uCnt] == '\r')
+ {
+ pchResponse[uCnt] = '\0';
+ return 0;
+ }
+ }
+
+ errno = EINVAL;
+ perror("bad response");
+ return 1;
+}
diff --git a/src/wacserial.h b/src/wacserial.h
new file mode 100644
index 0000000..f56058d
--- /dev/null
+++ b/src/wacserial.h
@@ -0,0 +1,115 @@
+/*****************************************************************************
+** wacserial.h
+**
+** Copyright (C) 2002 - John E. Joganic
+**
+** 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 Lesser General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+**
+****************************************************************************/
+
+#ifndef __WACPACK_WACSERIAL_H
+#define __WACPACK_WACSERIAL_H
+
+#define WACOMDEVICE_MASK 0x000F
+#define WACOMDEVICE_ARTPAD 0x0001
+#define WACOMDEVICE_ARTPADII 0x0002
+#define WACOMDEVICE_DIGITIZER 0x0003
+#define WACOMDEVICE_DIGITIZERII 0x0004
+#define WACOMDEVICE_PENPARTNER 0x0005
+#define WACOMDEVICE_GRAPHIRE 0x0006
+#define WACOMDEVICE_GRAPHIRE2 0x0007
+#define WACOMDEVICE_INTUOS 0x0008
+#define WACOMDEVICE_INTUOS2 0x0009
+#define WACOMDEVICE_CINTIQ 0x000A
+#define WACOMDEVICE(x) ((x)&WACOMDEVICE_MASK)
+
+#define WACOMCLASS_MASK 0x0300
+#define WACOMCLASS_SERIAL 0x0100
+#define WACOMCLASS_USB 0x0200
+#define WACOMCLASS(x) ((x)&WACOMCLASS_MASK)
+
+#define WACOMMODEL_SERIAL_INTUOS2 (WACOMCLASS_SERIAL|WACOMDEVICE_INTUOS2)
+typedef unsigned int WACOMMODEL;
+
+#define WACOMTOOLTYPE_NONE 0x00
+#define WACOMTOOLTYPE_PEN 0x01
+#define WACOMTOOLTYPE_PENCIL 0x02
+#define WACOMTOOLTYPE_BRUSH 0x03
+#define WACOMTOOLTYPE_ERASER 0x04
+#define WACOMTOOLTYPE_AIRBRUSH 0x05
+#define WACOMTOOLTYPE_MOUSE 0x06
+#define WACOMTOOLTYPE_LENS 0x07
+
+#define WACOMBUTTON_LEFT 0
+#define WACOMBUTTON_MIDDLE 1
+#define WACOMBUTTON_RIGHT 2
+#define WACOMBUTTON_EXTRA 3
+#define WACOMBUTTON_SIDE 4
+#define WACOMBUTTON_TOUCH 5
+#define WACOMBUTTON_STYLUS 6
+#define WACOMBUTTON_STYLUS2 7
+#define WACOMBUTTON_MAX 8
+
+#define WACOMFIELD_TOOLTYPE 0
+#define WACOMFIELD_SERIAL 1
+#define WACOMFIELD_PROXIMITY 2
+#define WACOMFIELD_BUTTONS 3
+#define WACOMFIELD_POSITION_X 4
+#define WACOMFIELD_POSITION_Y 5
+#define WACOMFIELD_ROTATION_Z 6
+#define WACOMFIELD_DISTANCE 7
+#define WACOMFIELD_PRESSURE 8
+#define WACOMFIELD_TILT_X 9
+#define WACOMFIELD_TILT_Y 10
+#define WACOMFIELD_ABSWHEEL 11
+#define WACOMFIELD_RELWHEEL 12
+#define WACOMFIELD_THROTTLE 13
+#define WACOMFIELD_MAX 14
+
+typedef struct
+{
+ int nValid;
+ int nToolType;
+ int nSerial;
+ int nProximity;
+ int nButtons;
+ int nPosX;
+ int nPosY;
+ int nRotZ;
+ int nDistance;
+ int nPressure;
+ int nTiltX;
+ int nTiltY;
+ int nAbsWheel;
+ int nRelWheel;
+ int nThrottle;
+} WACOMSTATE;
+
+typedef struct { int __unused; } *WACOMTABLET;
+
+WACOMTABLET WacomOpenSerial(const char* pszDevice);
+void WacomCloseSerial(WACOMTABLET hTablet);
+WACOMMODEL WacomGetModel(WACOMTABLET hTablet);
+const char* WacomGetModelName(WACOMTABLET hTablet);
+int WacomReadRaw(WACOMTABLET hTablet, unsigned char* puchData,
+ unsigned int uSize);
+int WacomGetRomVersion(WACOMTABLET hTablet, int* pnMajor, int* pnMinor,
+ int* pnRelease);
+int WacomGetCapabilities(WACOMTABLET hTablet);
+int WacomGetRanges(WACOMTABLET hTablet, WACOMSTATE* pMin, WACOMSTATE* pMax);
+int WacomParseData(WACOMTABLET hTablet, const unsigned char* puchData,
+ unsigned int uLength, WACOMSTATE* pState);
+
+#endif /* __WACPACK_WACSERIAL_H */
diff --git a/src/wcm-beta.c b/src/wcm-beta.c
new file mode 100644
index 0000000..babb2f6
--- /dev/null
+++ b/src/wcm-beta.c
@@ -0,0 +1,618 @@
+/*****************************************************************************
+ * wcm-beta.c
+ *
+ * Copyright 2002 by John Joganic <john@joganic.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * REVISION HISTORY
+ *
+ ****************************************************************************/
+
+#include "wcm-beta.h"
+
+/*****************************************************************************
+ * Notes:
+ *
+ * Namespaces - the public functions use "wcmBeta" as the namespace. This is
+ * to separate the XF86 module-level routines from the internal
+ * tablet-specific routines which are static and fall under the
+ * "wacom" namespace.
+ ****************************************************************************/
+
+
+#include "xf86.h"
+#include "keysym.h"
+#include "xf86_OSproc.h"
+#include "xf86Xinput.h"
+#include "exevents.h"
+
+/****************************************************************************
+** Defines
+****************************************************************************/
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(lvl, f) {if ((lvl) <= xnDebugLevel) f;}
+
+#ifdef SYSCALL
+#undef SYSCALL
+#endif
+#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
+
+#define WACOM_FLAG_BETA 128 /* must be same as xf86Wacom.c BETA_FLAG */
+
+/* Note: xf86 valuator conversion code does not handle more than 6 axes. */
+
+#define WACOM_AXIS_X 0
+#define WACOM_AXIS_Y 1
+#define WACOM_AXIS_PRESS 2
+#define WACOM_AXIS_TILTX 3
+#define WACOM_AXIS_TILTY 4
+#define WACOM_AXIS_WHEEL 5
+#define WACOM_AXIS_MAX 6
+
+#define WACOM_BUTTON_COUNT 16
+
+/*****************************************************************************
+** Structures
+*****************************************************************************/
+
+typedef struct _WacomCoord WacomCoord;
+typedef struct _WacomDevice WacomDevice;
+typedef struct _WacomDevice* WacomDevicePtr;
+
+struct _WacomCoord
+{
+ int nX;
+ int nY;
+ int nPress;
+ int nTiltX;
+ int nTiltY;
+ int nWheel;
+};
+
+struct _WacomDevice
+{
+ unsigned int uFlags; /* must be first for xf86Wacom.c compatibility */
+ InputDriverPtr pDriver; /* device driver */
+ char* pszDevicePath; /* device file name */
+ int nScreen; /* screen number for tablet */
+
+ /* active tablet window */
+ int nLeft;
+ int nTop;
+ int nRight;
+ int nBottom;
+
+ /* ratio of screen to active tablet window dimensions */
+ double dRatioX;
+ double dRatioY;
+
+ /* device capabilities */
+ WacomCoord min;
+ WacomCoord max;
+};
+
+/****************************************************************************
+** Globals
+****************************************************************************/
+
+ static int xnDebugLevel = 0;
+
+ static const char* xszDefaultOptions[] =
+ {
+ "BaudRate", "9600",
+ "StopBits", "1",
+ "DataBits", "8",
+ "Parity", "None",
+ "Vmin", "1",
+ "Vtime", "10",
+ "FlowControl", "Xoff",
+ NULL
+ };
+
+ static KeySym xSyms[] =
+ {
+ /* 0x00 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
+ /* 0x04 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
+ /* 0x08 */ XK_F1, XK_F2, XK_F3, XK_F4,
+ /* 0x0C */ XK_F5, XK_F6, XK_F7, XK_F8,
+ /* 0x10 */ XK_F9, XK_F10, XK_F11, XK_F12,
+ /* 0x14 */ XK_F13, XK_F14, XK_F15, XK_F16,
+ /* 0x18 */ XK_F17, XK_F18, XK_F19, XK_F20,
+ /* 0x1C */ XK_F21, XK_F22, XK_F23, XK_F24,
+ /* 0x20 */ XK_F25, XK_F26, XK_F27, XK_F28,
+ /* 0x24 */ XK_F29, XK_F30, XK_F31, XK_F32
+ };
+
+ static KeySymsRec xKeySyms =
+ {
+ xSyms, /* map */
+ 0x08, /* minimum key code */
+ (sizeof(xSyms) / sizeof(*xSyms)) - 1, /* maximum key code */
+ 1, /* character width in bytes */
+ };
+
+/****************************************************************************
+** Static Prototypes
+****************************************************************************/
+
+static void wacomFreeDevice(LocalDevicePtr pDevice);
+static int wacomConfigDevice(InputDriverPtr pDriver, LocalDevicePtr pDevice,
+ IDevPtr pConfig);
+static int wacomDeviceCtrlProc(DeviceIntPtr pInt, int nWhat);
+static void wacomDeviceFeedbackCtrlProc(DeviceIntPtr pInt, PtrCtrl* pCtrl);
+static int wacomInitDevice(LocalDevicePtr pDevice);
+static int wacomEnableDevice(LocalDevicePtr pDevice);
+static int wacomDisableDevice(LocalDevicePtr pDevice);
+static void wacomCloseDevice(LocalDevicePtr pDevice);
+static Bool wacomConvertDeviceValuators(LocalDevicePtr pDevice,
+ int nFirst, int nNum, int v0, int v1, int v2, int v3,
+ int v4, int v5, int* px, int* py);
+static Bool wacomReverseDeviceValuators(LocalDevicePtr pDevice, int x, int y,
+ int* pValuators);
+static int wacomSwitchDeviceMode(ClientPtr pClient, DeviceIntPtr pInt,
+ int nMode);
+static int wacomControlDevice(LocalDevicePtr pDevice, xDeviceCtl* pCtrl);
+static int wacomOpenDevice(DeviceIntPtr pInt);
+static void wacomReadInput(LocalDevicePtr pDevice);
+
+/*****************************************************************************
+** Device initialization and termination
+*****************************************************************************/
+
+InputInfoPtr wcmBetaNewDevice(InputDriverPtr pDriver, IDevPtr pConfig,
+ int nFlags)
+{
+ LocalDevicePtr pDevice;
+ WacomDevicePtr pPriv;
+
+ xf86Msg(X_CONFIG, "wcmBetaInitDevice: drv=%d %s %p %d\n",
+ pDriver->driverVersion,
+ pDriver->driverName,
+ pDriver->module,
+ pDriver->refCount);
+ xf86Msg(X_CONFIG, "wcmBetaInitDevice: config=%s %s\n",
+ pConfig->identifier,
+ pConfig->driver);
+ xf86Msg(X_CONFIG, "wcmBetaInitDevice: flags=%08X\n",nFlags);
+
+ /* allocate private structure */
+ pPriv = (WacomDevicePtr) xalloc(sizeof(WacomDevice));
+ if (!pPriv) return NULL;
+ memset(pPriv,0,sizeof(pPriv));
+
+ /* allocate input device and add to list */
+ pDevice = xf86AllocateInput(pDriver, 0);
+ if (!pDevice)
+ {
+ xfree(pPriv);
+ return NULL;
+ }
+
+ /* initialize and configure device */
+ pDevice->private = pPriv;
+ if (wacomConfigDevice(pDriver,pDevice,pConfig))
+ {
+ wacomFreeDevice(pDevice);
+ return NULL;
+ }
+
+ return pDevice;
+}
+
+void wcmBetaDeleteDevice(InputDriverPtr pDriver, LocalDevicePtr pDevice,
+ int nFlags)
+{
+ xf86Msg(X_CONFIG, "wcmBetaDeleteDevice: drv=%d %s %p %d\n",
+ pDriver->driverVersion,
+ pDriver->driverName,
+ pDriver->module,
+ pDriver->refCount);
+ xf86Msg(X_CONFIG, "wcmBetaDeleteDevice: dev=%s\n",pDevice->name);
+ xf86Msg(X_CONFIG, "wcmBetaDeleteDevice: flags=%08X\n",nFlags);
+
+ /* disable and deallocate device */
+ wacomDisableDevice(pDevice);
+ wacomFreeDevice(pDevice);
+}
+
+/*****************************************************************************
+** Implementation
+*****************************************************************************/
+
+static int wacomConfigDevice(InputDriverPtr pDriver, LocalDevicePtr pDevice,
+ IDevPtr pConfig)
+{
+ WacomDevicePtr pPriv = (WacomDevicePtr) pDevice->private;
+
+ xf86Msg(X_INFO, "wacomConfigDevice\n");
+
+ /* Initialize XF86 local device structure */
+ pDevice->name = "TABLET";
+ pDevice->type_name = "Wacom Tablet";
+ pDevice->flags = 0;
+ pDevice->device_control = wacomDeviceCtrlProc;
+ pDevice->read_input = wacomReadInput;
+ pDevice->control_proc = wacomControlDevice;
+ pDevice->close_proc = wacomCloseDevice;
+ pDevice->switch_mode = wacomSwitchDeviceMode;
+ pDevice->conversion_proc = wacomConvertDeviceValuators;
+ pDevice->reverse_conversion_proc = wacomReverseDeviceValuators;
+ pDevice->fd = -1;
+ pDevice->atom = 0;
+ pDevice->dev = NULL;
+ pDevice->private_flags = 0;
+ pDevice->history_size = 0;
+ pDevice->old_x = -1;
+ pDevice->old_y = -1;
+
+ /* Initialize private structure */
+ memset(pPriv,0,sizeof(*pPriv));
+ pPriv->uFlags = WACOM_FLAG_BETA;
+ pPriv->pDriver = pDriver;
+ pPriv->pszDevicePath = "";
+ pPriv->nScreen = -1;
+ pPriv->nLeft = pPriv->nTop = pPriv->nRight = pPriv->nBottom = -1;
+
+ /* retrieve values from configuration */
+ pDevice->name = pConfig->identifier;
+ pDevice->conf_idev = pConfig;
+ xf86CollectInputOptions(pDevice, xszDefaultOptions, NULL);
+
+ /* Handle common options like AlwaysCore, CorePointer, HistorySize, ... */
+ xf86ProcessCommonOptions(pDevice, pDevice->options);
+
+ /* Get the debugging log level */
+ xnDebugLevel = xf86SetIntOption(pDevice->options,
+ "DebugLevel", xnDebugLevel);
+
+ if (xnDebugLevel > 0)
+ xf86Msg(X_CONFIG, "wacomConfigDevice: debug level set to %d\n",
+ xnDebugLevel);
+
+ /* Get tablet device (eg. /dev/ttyS0 or /dev/input/event0) */
+ pPriv->pszDevicePath = xf86FindOptionValue(pDevice->options, "Device");
+ if (!pPriv->pszDevicePath)
+ {
+ xf86Msg (X_ERROR, "wacomConfigDevice: No device specified for %s.\n",
+ pDevice->name);
+ return 1;
+ }
+ else
+ xf86Msg(X_CONFIG, "wacomConfigDevice: %s device is %s\n",
+ pDevice->name, pPriv->pszDevicePath);
+
+ /* Get screen number, if provided */
+ pPriv->nScreen = xf86SetIntOption(pDevice->options, "ScreenNo", -1);
+ if (pPriv->nScreen != -1)
+ {
+ xf86Msg(X_CONFIG, "wacomConfigDevice: %s device on screen %d\n",
+ pDevice->name, pPriv->nScreen);
+ }
+
+ /* Mark device as fully configured and ready */
+ pDevice->flags = XI86_POINTER_CAPABLE | XI86_CONFIGURED;
+
+ return 0;
+}
+
+static void wacomFreeDevice(LocalDevicePtr pDevice)
+{
+ WacomDevicePtr pPriv = (WacomDevicePtr) pDevice->private;
+
+ DBG(1,xf86Msg(X_INFO, "wacomFreeDevice: %s\n",pDevice->name));
+
+ xfree(pPriv);
+ pDevice->private = NULL;
+ xf86DeleteInput(pDevice,0);
+}
+
+static int wacomDeviceCtrlProc(DeviceIntPtr pInt, int nWhat)
+{
+ LocalDevicePtr pDevice = (LocalDevicePtr) pInt->public.devicePrivate;
+
+ switch (nWhat)
+ {
+ case DEVICE_INIT: return wacomInitDevice(pDevice);
+ case DEVICE_ON: return wacomEnableDevice(pDevice);
+ case DEVICE_OFF: return wacomDisableDevice(pDevice);
+ case DEVICE_CLOSE: wacomCloseDevice(pDevice); return Success;
+ }
+
+ xf86Msg(X_ERROR,"wacomDeviceCtrlProc: %s received bad request %d\n",
+ pDevice->name,nWhat);
+
+ return !Success;
+}
+
+static int wacomInitDevice(LocalDevicePtr pDevice)
+{
+ int i;
+ CARD8 uchMap[WACOM_BUTTON_COUNT];
+
+ DBG(1,xf86Msg(X_INFO, "wacomInitDevice: %s\n",pDevice->name));
+
+ /* Initialize for everything - too much in fact.
+ * The individual pointer types will be more specialized. */
+
+ /* we report button events */
+ for (i=1; i<=WACOM_BUTTON_COUNT; ++i) uchMap[i] = i;
+ if (!InitButtonClassDeviceStruct(pDevice->dev, WACOM_BUTTON_COUNT, uchMap))
+ {
+ xf86Msg(X_ERROR, "wacomInitDevice: "
+ "failed to init as button class device\n");
+ return !Success;
+ }
+
+ /* we report key events */
+ if (!InitKeyClassDeviceStruct(pDevice->dev, &xKeySyms, NULL))
+ {
+ xf86Msg(X_ERROR, "wacomInitDevice: "
+ "failed to init as key class device\n");
+ return !Success;
+ }
+
+ /* we can be focused */
+ if (!InitFocusClassDeviceStruct(pDevice->dev))
+ {
+ xf86Msg(X_ERROR, "wacomInitDevice: "
+ "failed to init as focus class device\n");
+ return !Success;
+ }
+
+ /* we support feedbacks - sort of */
+ if (!InitPtrFeedbackClassDeviceStruct(pDevice->dev,
+ wacomDeviceFeedbackCtrlProc))
+ {
+ xf86Msg(X_ERROR, "wacomInitDevice: "
+ "failed to init as feedback class device\n");
+ return !Success;
+ }
+
+ /* we report proximity events */
+ if (!InitProximityClassDeviceStruct(pDevice->dev))
+ {
+ xf86Msg(X_ERROR, "wacomInitDevice: "
+ "failed to init as proximity class device\n");
+ return !Success;
+ }
+
+ /* we report valuator data in motion events */
+ if (!InitValuatorClassDeviceStruct(pDevice->dev, WACOM_AXIS_MAX,
+ xf86GetMotionEvents, pDevice->history_size,
+ OutOfProximity | Absolute))
+ {
+ xf86Msg(X_ERROR, "wacomInitDevice: "
+ "failed to init as valuator class device\n");
+ return !Success;
+ }
+
+ /* allocate the motion history buffer */
+ xf86MotionHistoryAllocate(pDevice);
+
+ /* open the device to gather informations */
+ return wacomOpenDevice(pDevice->dev);
+}
+
+static int wacomEnableDevice(LocalDevicePtr pDevice)
+{
+ DBG(1,xf86Msg(X_INFO, "wacomEnableDevice: %s\n",pDevice->name));
+ if (pDevice->fd < 0)
+ {
+ if (wacomOpenDevice(pDevice->dev) != Success)
+ return !Success;
+ xf86AddEnabledDevice(pDevice);
+ }
+
+ pDevice->dev->public.on = TRUE;
+ return Success;
+}
+
+static int wacomDisableDevice(LocalDevicePtr pDevice)
+{
+ DBG(1,xf86Msg(X_INFO, "wacomDisableDevice: %s\n",pDevice->name));
+ if (pDevice->fd >= 0)
+ {
+ xf86RemoveEnabledDevice(pDevice);
+ SYSCALL(xf86CloseSerial(pDevice->fd));
+ pDevice->fd = -1;
+ }
+
+ pDevice->dev->public.on = FALSE;
+ return Success;
+}
+
+static void wacomCloseDevice(LocalDevicePtr pDevice)
+{
+ DBG(1,xf86Msg(X_INFO, "wacomCloseDevice: %s\n",pDevice->name));
+ if (pDevice->fd >= 0) wacomDisableDevice(pDevice);
+}
+
+static void wacomDeviceFeedbackCtrlProc(DeviceIntPtr pInt, PtrCtrl* pCtrl)
+{
+ DBG(1, xf86Msg(X_INFO, "wacomDeviceFeedbackCtrlProc\n"));
+}
+
+static Bool wacomConvertDeviceValuators(LocalDevicePtr pDevice,
+ int nFirst, int nNum, int v0, int v1, int v2, int v3,
+ int v4, int v5, int* px, int* py)
+{
+ WacomDevicePtr pPriv = (WacomDevicePtr)pDevice->private;
+
+ /* what does this do? */
+ if (nFirst != 0 || nNum == 1) return FALSE;
+
+ /* screen coordinates are based solely on X and Y axis */
+
+ *px = (int)(v0 * pPriv->dRatioX + 0.5); /* WACOM_X_AXIS */
+ *py = (int)(v1 * pPriv->dRatioY + 0.5); /* WACOM_Y_AXIS */
+
+ return TRUE;
+}
+
+static Bool wacomReverseDeviceValuators(LocalDevicePtr pDevice, int x, int y,
+ int* pValuators)
+{
+ WacomDevicePtr pPriv = (WacomDevicePtr)pDevice->private;
+
+ /* screen coordinates are based solely on X and Y axis */
+
+ pValuators[WACOM_AXIS_X] = (int)(x / pPriv->dRatioX + 0.5);
+ pValuators[WACOM_AXIS_Y] = (int)(y / pPriv->dRatioY + 0.5);
+
+ return TRUE;
+}
+
+static int wacomSwitchDeviceMode(ClientPtr pClient, DeviceIntPtr pInt,
+ int nMode)
+{
+ LocalDevicePtr pDevice = (LocalDevicePtr) pInt->public.devicePrivate;
+
+ DBG(3, xf86Msg(X_INFO, "wacomSwitchDeviceMode %s mode=%s\n",
+ pDevice->name, (nMode == Absolute) ? "ABS" :
+ (nMode == Relative) ? "REL" : "UNK"));
+
+ /* ignore */
+ return Success;
+}
+
+static int wacomControlDevice(LocalDevicePtr pDevice, xDeviceCtl* pCtrl)
+{
+ /* no controls defined */
+ return Success;
+}
+
+static int wacomOpenDevice(DeviceIntPtr pInt)
+{
+ LocalDevicePtr pDevice = (LocalDevicePtr)pInt->public.devicePrivate;
+ WacomDevicePtr pPriv = (WacomDevicePtr)pDevice->private;
+ ScreenPtr pScreen;
+
+ xf86Msg(X_INFO, "wacomOpenDevice\n");
+
+ /* open device as serial line */
+ if (pDevice->fd < 0)
+ {
+ pDevice->fd = xf86OpenSerial(pDevice->options);
+ if (pDevice->fd < 0)
+ return !Success;
+ }
+
+ /* Query device capabilities for hypothetical 6x4 tablet */
+ pPriv->min.nX = 0;
+ pPriv->max.nX = 15240;
+ pPriv->min.nY = 0;
+ pPriv->max.nY = 10160;
+ pPriv->min.nPress = 0;
+ pPriv->max.nPress = 1023;
+ pPriv->min.nTiltX = -64;
+ pPriv->max.nTiltX = 63;
+ pPriv->min.nTiltY = -64;
+ pPriv->max.nTiltY = 63;
+ pPriv->min.nWheel = 0;
+ pPriv->max.nWheel = 1023;
+
+ /* set bounds to defaults, if not specified by configuration */
+ if (pPriv->nLeft < 0) pPriv->nLeft = pPriv->min.nX;
+ if (pPriv->nTop < 0) pPriv->nTop = pPriv->min.nY;
+ if (pPriv->nRight < 0) pPriv->nRight = pPriv->max.nX;
+ if (pPriv->nBottom < 0) pPriv->nBottom = pPriv->max.nY;
+
+ /* validate left bound */
+ if ((pPriv->nLeft > pPriv->max.nX || pPriv->nLeft < pPriv->min.nX))
+ pPriv->nLeft = pPriv->min.nX;
+
+ /* validate top bound */
+ if ((pPriv->nTop > pPriv->max.nY || pPriv->nTop < pPriv->min.nY))
+ pPriv->nTop = pPriv->min.nY;
+
+ /* validate right bound */
+ if ((pPriv->nRight > pPriv->max.nX || pPriv->nRight < pPriv->min.nX))
+ pPriv->nRight = pPriv->max.nX;
+
+ /* validate bottom bound */
+ if ((pPriv->nBottom > pPriv->max.nY || pPriv->nBottom < pPriv->min.nY))
+ pPriv->nBottom = pPriv->max.nY;
+
+ /* validate width */
+ if (pPriv->nLeft >= pPriv->nRight)
+ {
+ pPriv->nLeft = pPriv->min.nX;
+ pPriv->nRight = pPriv->max.nX;
+ }
+
+ /* validate height */
+ if (pPriv->nTop >= pPriv->nBottom)
+ {
+ pPriv->nTop = pPriv->min.nY;
+ pPriv->nBottom = pPriv->max.nY;
+ }
+
+ xf86Msg(X_INFO, "wacomOpenDevice: %s bounds %d,%d %d,%d\n",
+ pDevice->name, pPriv->nLeft, pPriv->nTop,
+ pPriv->nRight, pPriv->nBottom);
+
+ /* get screen */
+ if (pPriv->nScreen < 0) pPriv->nScreen = 0;
+ if (pPriv->nScreen >= screenInfo.numScreens)
+ {
+ xf86Msg(X_INFO,"wacomOpenDevice: %s on invalid screen %d, reset to 0\n",
+ pDevice->name, pPriv->nScreen);
+ pPriv->nScreen = 0;
+ }
+ pScreen = screenInfo.screens[pPriv->nScreen];
+
+ xf86Msg(X_INFO, "wacomOpenDevice: %s on screen %d (%d, %d)\n",
+ pDevice->name, pPriv->nScreen, pScreen->width, pScreen->height);
+
+ /* calculate screen to tablet ratios */
+ pPriv->dRatioX = (double)pScreen->width / (pPriv->nRight - pPriv->nLeft);
+ pPriv->dRatioY = (double)pScreen->height / (pPriv->nBottom - pPriv->nTop);
+
+ xf86Msg(X_INFO, "wacomOpenDevice: %s scaled %.4g, %.4g\n",
+ pDevice->name, pPriv->dRatioX, pPriv->dRatioY);
+
+ /* Initialize valuators */
+ InitValuatorAxisStruct(pInt,WACOM_AXIS_X,
+ 0, pPriv->nRight - pPriv->nLeft,
+ 100000,0,100000);
+ InitValuatorAxisStruct(pInt,WACOM_AXIS_Y,
+ 0, pPriv->nBottom - pPriv->nTop,
+ 100000,0,100000);
+ InitValuatorAxisStruct(pInt,WACOM_AXIS_PRESS,
+ pPriv->min.nPress, pPriv->max.nPress,
+ 100000,0,100000);
+ InitValuatorAxisStruct(pInt,WACOM_AXIS_TILTX,
+ pPriv->min.nTiltX, pPriv->max.nTiltX,
+ 100000,0,100000);
+ InitValuatorAxisStruct(pInt,WACOM_AXIS_TILTY,
+ pPriv->min.nTiltY, pPriv->max.nTiltY,
+ 100000,0,100000);
+ InitValuatorAxisStruct(pInt,WACOM_AXIS_WHEEL,
+ pPriv->min.nWheel, pPriv->max.nWheel,
+ 100000,0,100000);
+ return (pDevice->fd >= 0) ? Success : !Success;
+}
+
+static void wacomReadInput(LocalDevicePtr pDevice)
+{
+ int nLen;
+ unsigned char chBuf[256];
+
+ /* read and ignore for now */
+ SYSCALL(nLen = xf86ReadSerial(pDevice->fd, chBuf, sizeof(chBuf)));
+ return;
+}
+
+
diff --git a/src/wcm-beta.h b/src/wcm-beta.h
new file mode 100644
index 0000000..f3bafcc
--- /dev/null
+++ b/src/wcm-beta.h
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * wcm-beta.h
+ *
+ * Copyright 2002 by John Joganic <john@joganic.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * REVISION HISTORY
+ *
+ ****************************************************************************/
+
+#ifndef __XF86_WCM_BETA_H
+#define __XF86_WCM_BETA_H
+
+#include "xf86Version.h"
+#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(3,9,0,0,0)
+#error This code will not work on XFree86 version 3.x, yet
+#endif
+
+/* FOO **********************************************************************/
+
+#ifdef LINUX_INPUT
+#include <asm/types.h>
+#include <linux/input.h>
+
+/* 2.4.5 module support */
+#ifndef EV_MSC
+#define EV_MSC 0x04
+#endif
+
+#ifndef MSC_SERIAL
+#define MSC_SERIAL 0x00
+#endif
+
+/* max number of input events to read in one read call */
+#define MAX_EVENTS 50
+
+/* keithp - a hack to avoid redefinitions of these in xf86str.h */
+#ifdef BUS_PCI
+#undef BUS_PCI
+#endif
+#ifdef BUS_ISA
+#undef BUS_ISA
+#endif
+#endif /* LINUX_INPUT */
+
+/* BAR ***********************************************************************/
+
+#include <xf86Xinput.h>
+
+/***************************************************************************/
+
+InputInfoPtr wcmBetaNewDevice(InputDriverPtr pDriver, IDevPtr pConfig,
+ int nFlags);
+void wcmBetaDeleteDevice(InputDriverPtr pDriver, LocalDevicePtr pDevice,
+ int nFlags);
+
+/***************************************************************************/
+#endif /* __XF86_WCM_BETA_H */
diff --git a/src/xf86Wacom.c b/src/xf86Wacom.c
new file mode 100644
index 0000000..7924bd4
--- /dev/null
+++ b/src/xf86Wacom.c
@@ -0,0 +1,4070 @@
+/* $XConsortium: xf86Wacom.c /main/20 1996/10/27 11:05:20 kaleb $ */
+/*
+ * Copyright 1995-2002 by Frederic Lepied, France. <Lepied@XFree86.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Frederic Lepied not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Frederic Lepied makes no
+ * representations about the suitability of this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ *
+ * FREDERIC LEPIED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL FREDERIC LEPIED BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/* $XFree86: xc/programs/Xserver/hw/xfree86/input/wacom/xf86Wacom.c,v 1.26 2001/04/01 14:00:13 tsi Exp $ */
+
+/*
+ * This driver is only able to handle the Wacom IV and Wacom V protocols.
+ *
+ * Wacom V protocol work done by Raph Levien <raph@gtk.org> and
+ * Frédéric Lepied <lepied@xfree86.org>.
+ *
+ * Many thanks to Dave Fleck from Wacom for the help provided to
+ * build this driver.
+ *
+ * Modified for Linux USB by MATSUMURA Namihiko,
+ * Daniel Egger, Germany. <egger@suse.de>,
+ * Frederic Lepied <lepied@xfree86.org>,
+ * Brion Vibber <brion@pobox.com>,
+ * Aaron Optimizer Digulla <digulla@hepe.com>,
+ * Jonathan Layes <jonathan@layes.com>.
+ *
+ */
+
+/*
+ * TO XFREE86 THE MAINTAINERS: don't remove the 3.3 code as I continue to maintain it.
+ * Discuss with me before changing things in this driver!
+ *
+ * Fred
+ */
+
+/*
+ * REVISION HISTORY
+ *
+ * 2002-12-17 26-j0.3.3 - added Intuos2
+ */
+
+static const char identification[] = "$Identification: 26-j0.3.3 $";
+
+#include "xf86Version.h"
+
+#if XF86_VERSION_CURRENT >= XF86_VERSION_NUMERIC(3,9,0,0,0)
+#define XFREE86_V4 1
+#endif
+
+#ifdef LINUX_INPUT
+#include <asm/types.h>
+#include <linux/input.h>
+
+/* 2.4.5 module support */
+#ifndef EV_MSC
+#define EV_MSC 0x04
+#endif
+
+#ifndef MSC_SERIAL
+#define MSC_SERIAL 0x00
+#endif
+
+/* max number of input events to read in one read call */
+#define MAX_EVENTS 50
+
+/* keithp - a hack to avoid redefinitions of these in xf86str.h */
+#ifdef BUS_PCI
+#undef BUS_PCI
+#endif
+#ifdef BUS_ISA
+#undef BUS_ISA
+#endif
+#endif
+
+#ifdef XFREE86_V4
+/* post 3.9 headers */
+
+#ifndef XFree86LOADER
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#include "misc.h"
+#include "xf86.h"
+#define NEED_XF86_TYPES
+#if !defined(DGUX)
+#include "xf86_ansic.h"
+#include "xisb.h"
+#endif
+#include "xf86_OSproc.h"
+#include "xf86Xinput.h"
+#include "exevents.h" /* Needed for InitValuator/Proximity stuff */
+#include "keysym.h"
+#include "mipointer.h"
+
+#ifdef XFree86LOADER
+#include "xf86Module.h"
+#endif
+
+#undef sleep
+#define sleep(t) xf86WaitForInput(-1, 1000 * (t))
+#define wait_for_fd(fd) xf86WaitForInput((fd), 1000000)
+#define tcflush(fd, n) xf86FlushInput((fd))
+#undef read
+#define read(a,b,c) xf86ReadSerial((a),(b),(c))
+#undef write
+#define write(a,b,c) xf86WriteSerial((a),(char*)(b),(c))
+#undef close
+#define close(a) xf86CloseSerial((a))
+#define XCONFIG_PROBED "(==)"
+#define XCONFIG_GIVEN "(**)"
+#define xf86Verbose 1
+#undef PRIVATE
+#define PRIVATE(x) XI_PRIVATE(x)
+
+/*
+ * Be sure to set vmin appropriately for your device's protocol. You want to
+ * read a full packet before returning
+ */
+static const char *default_options[] =
+{
+ "BaudRate", "9600",
+ "StopBits", "1",
+ "DataBits", "8",
+ "Parity", "None",
+ "Vmin", "1",
+ "Vtime", "10",
+ "FlowControl", "Xoff",
+ NULL
+};
+
+static InputDriverPtr wcmDrv;
+
+#else /* pre 3.9 headers */
+
+#include "Xos.h"
+#include <signal.h>
+#include <stdio.h>
+
+#define NEED_EVENTS
+#include "X.h"
+#include "Xproto.h"
+#include "misc.h"
+#include "inputstr.h"
+#include "scrnintstr.h"
+#include "XI.h"
+#include "XIproto.h"
+#include "keysym.h"
+
+#if defined(sun) && !defined(i386)
+#define POSIX_TTY
+#include <errno.h>
+#include <termio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "extio.h"
+#else
+#include "compiler.h"
+
+#include "xf86.h"
+#include "xf86Procs.h"
+#include "xf86_OSlib.h"
+#include "xf86_Config.h"
+#include "xf86Xinput.h"
+#include "atKeynames.h"
+#include "xf86Version.h"
+#endif
+
+#if !defined(sun) || defined(i386)
+#include "osdep.h"
+#include "exevents.h"
+
+#include "extnsionst.h"
+#include "extinit.h"
+#endif
+
+#endif /* Pre 3.9 headers */
+
+#if defined(__QNX__) || defined(__QNXNTO__)
+#define POSIX_TTY
+#endif
+
+/******************************************************************************
+ * debugging macro
+ *****************************************************************************/
+#ifdef DBG
+#undef DBG
+#endif
+#ifdef DEBUG
+#undef DEBUG
+#endif
+
+static int debug_level = 0;
+#define DEBUG 1
+#if DEBUG
+#define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
+#else
+#define DBG(lvl, f)
+#endif
+
+#define ABS(x) ((x) > 0 ? (x) : -(x))
+
+/******************************************************************************
+ * WacomDeviceRec flags
+ *****************************************************************************/
+#define DEVICE_ID(flags) ((flags) & 0x07)
+
+#define STYLUS_ID 1
+#define CURSOR_ID 2
+#define ERASER_ID 4
+#define ABSOLUTE_FLAG 8
+#define FIRST_TOUCH_FLAG 16
+#define KEEP_SHAPE_FLAG 32
+#define BAUD_19200_FLAG 64
+#define BETA_FLAG 128
+
+/******************************************************************************
+ * WacomCommonRec flags
+ *****************************************************************************/
+#define TILT_FLAG 1
+#define GRAPHIRE_FLAG 2
+
+typedef struct
+{
+ int state;
+ int coord[3];
+ int tilt[3];
+} WacomFilterState;
+
+typedef struct
+{
+ int device_id;
+ int device_type;
+ unsigned int serial_num;
+ int x;
+ int y;
+ int buttons;
+ int pressure;
+ int tiltx;
+ int tilty;
+ int rotation;
+ int wheel;
+ int discard_first;
+ int proximity;
+ WacomFilterState x_filter;
+ WacomFilterState y_filter;
+} WacomDeviceState;
+
+#define PEN(ds) (((ds->device_id) & 0x07ff) == 0x0022 || ((ds->device_id) & 0x07ff) == 0x0052)
+#define STROKING_PEN(ds) (((ds->device_id) & 0x07ff) == 0x0032)
+#define AIRBRUSH(ds) (((ds->device_id) & 0x07ff) == 0x0112)
+#define MOUSE_4D(ds) (((ds->device_id) & 0x07ff) == 0x0094)
+#define LENS_CURSOR(ds) (((ds->device_id) & 0x07ff) == 0x0096)
+#define INKING_PEN(ds) (((ds->device_id) & 0x07ff) == 0x0012)
+
+typedef struct
+{
+ /* configuration fields */
+ unsigned int flags; /* various flags (device type, absolute, first touch...) */
+ int topX; /* X top */
+ int topY; /* Y top */
+ int bottomX; /* X bottom */
+ int bottomY; /* Y bottom */
+ double factorX; /* X factor */
+ double factorY; /* Y factor */
+ unsigned int serial; /* device serial number */
+ int initNumber; /* magic number for the init phasis */
+ int screen_no; /* associated screen */
+
+ struct _WacomCommonRec *common; /* common info pointer */
+
+ /* state fields */
+ int oldX; /* previous X position */
+ int oldY; /* previous Y position */
+ int oldZ; /* previous pressure */
+ int oldTiltX; /* previous tilt in x direction */
+ int oldTiltY; /* previous tilt in y direction */
+ int oldWheel; /* previous wheel value */
+ int oldButtons; /* previous buttons state */
+ int oldProximity; /* previous proximity */
+
+ /* JEJ - throttle */
+ int throttleStart; /* time in ticks for last wheel movement */
+ int throttleLimit; /* time in ticks for next wheel movement */
+ int throttleValue; /* current throttle value */
+
+} WacomDeviceRec, *WacomDevicePtr;
+
+#define MAX_USB_EVENTS 6
+
+typedef struct _WacomCommonRec
+{
+ char *wcmDevice; /* device file name */
+ int wcmSuppress; /* transmit position if increment is superior */
+ unsigned char wcmFlags; /* various flags (handle tilt) */
+ int wcmMaxX; /* max X value */
+ int wcmMaxY; /* max Y value */
+ int wcmMaxZ; /* max Z value */
+ int wcmResolX; /* X resolution in points/inch */
+ int wcmResolY; /* Y resolution in points/inch */
+ int wcmResolZ; /* Z resolution in points/inch */
+ LocalDevicePtr *wcmDevices; /* array of all devices sharing the same port */
+ int wcmNumDevices; /* number of devices */
+ int wcmIndex; /* number of bytes read */
+ int wcmPktLength; /* length of a packet */
+ unsigned char wcmData[9]; /* data read on the device */
+ Bool wcmHasEraser; /* True if an eraser has been configured */
+ Bool wcmStylusSide; /* eraser or stylus ? */
+ Bool wcmStylusProximity; /* the stylus is in proximity ? */
+ int wcmProtocolLevel; /* 4 for Wacom IV, 5 for Wacom V */
+ int wcmThreshold; /* Threshold for counting pressure as a button */
+ WacomDeviceState wcmDevStat[2]; /* device state for each tool */
+ int wcmInitNumber; /* magic number for the init phasis */
+ unsigned int wcmLinkSpeed; /* serial link speed */
+ Bool (*wcmOpen)(LocalDevicePtr /*local*/); /* function used to open the line (serial or USB) */
+ unsigned int wcmLastSerial; /* last device (used by the USB part) */
+ int wcmLastTool; /* last tool (used by USB part) */
+#ifdef LINUX_INPUT
+ struct input_event wcmEvent[MAX_USB_EVENTS]; /* data used by USB driver */
+#endif
+} WacomCommonRec, *WacomCommonPtr;
+
+/******************************************************************************
+ * configuration stuff
+ *****************************************************************************/
+#define CURSOR_SECTION_NAME "wacomcursor"
+#define STYLUS_SECTION_NAME "wacomstylus"
+#define ERASER_SECTION_NAME "wacomeraser"
+
+#ifndef XFREE86_V4
+
+#define PORT 1
+#define DEVICENAME 2
+#define THE_MODE 3
+#define SUPPRESS 4
+#define DEBUG_LEVEL 5
+#define TILT_MODE 6
+#define HISTORY_SIZE 7
+#define ALWAYS_CORE 8
+#define KEEP_SHAPE 9
+#define TOP_X 10
+#define TOP_Y 11
+#define BOTTOM_X 12
+#define BOTTOM_Y 13
+#define SERIAL 14
+#define BAUD_RATE 15
+#define THRESHOLD 16
+#define MAX_X 17
+#define MAX_Y 18
+#define MAX_Z 19
+#define RESOLUTION_X 20
+#define RESOLUTION_Y 21
+#define RESOLUTION_Z 22
+#define USB 23
+#define SCREEN_NO 24
+
+#if !defined(sun) || defined(i386)
+static SymTabRec WcmTab[] = {
+ { ENDSUBSECTION, "endsubsection" },
+ { PORT, "port" },
+ { DEVICENAME, "devicename" },
+ { THE_MODE, "mode" },
+ { SUPPRESS, "suppress" },
+ { DEBUG_LEVEL, "debuglevel" },
+ { TILT_MODE, "tiltmode" },
+ { HISTORY_SIZE, "historysize" },
+ { ALWAYS_CORE, "alwayscore" },
+ { KEEP_SHAPE, "keepshape" },
+ { TOP_X, "topx" },
+ { TOP_Y, "topy" },
+ { BOTTOM_X, "bottomx" },
+ { BOTTOM_Y, "bottomy" },
+ { SERIAL, "serial" },
+ { BAUD_RATE, "baudrate" },
+ { THRESHOLD, "threshold" },
+ { MAX_X, "maxx" },
+ { MAX_Y, "maxy" },
+ { MAX_Z, "maxz" },
+ { RESOLUTION_X, "resolutionx" },
+ { RESOLUTION_Y, "resolutiony" },
+ { RESOLUTION_Z, "resolutionz" },
+ { USB, "usb" },
+ { SCREEN_NO, "screenno" },
+ { -1, "" }
+};
+
+#define RELATIVE 1
+#define ABSOLUTE 2
+
+static SymTabRec ModeTabRec[] = {
+ { RELATIVE, "relative" },
+ { ABSOLUTE, "absolute" },
+ { -1, "" }
+};
+
+#endif
+
+#endif /* Pre 3.9 headers */
+
+/******************************************************************************
+ * constant and macros declarations
+ *****************************************************************************/
+#define DEFAULT_MAXZ 240 /* default value of MaxZ when nothing is configured */
+#define BUFFER_SIZE 256 /* size of reception buffer */
+#define XI_STYLUS "STYLUS" /* X device name for the stylus */
+#define XI_CURSOR "CURSOR" /* X device name for the cursor */
+#define XI_ERASER "ERASER" /* X device name for the eraser */
+#define MAX_VALUE 100 /* number of positions */
+#define MAXTRY 3 /* max number of try to receive magic number */
+#define MAX_COORD_RES 1270.0 /* Resolution of the returned MaxX and MaxY */
+#define INVALID_THRESHOLD 30000 /* Invalid threshold value used to test if the user
+ * configured it or not */
+
+#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
+
+#define WC_RESET "\r#" /* reset to wacom IV command set or wacom V reset */
+#define WC_RESET_BAUD "\r$" /* reset baud rate to default (wacom V) or switch to wacom IIs (wacom IV) */
+#define WC_CONFIG "~R\r" /* request a configuration string */
+#define WC_COORD "~C\r" /* request max coordinates */
+#define WC_MODEL "~#\r" /* request model and ROM version */
+
+#define WC_MULTI "MU1\r" /* multi mode input */
+#define WC_UPPER_ORIGIN "OC1\r" /* origin in upper left */
+#define WC_SUPPRESS "SU" /* suppress mode */
+#define WC_ALL_MACRO "~M0\r" /* enable all macro buttons */
+#define WC_NO_MACRO1 "~M1\r" /* disable macro buttons of group 1 */
+#define WC_RATE "IT0\r" /* max transmit rate (unit of 5 ms) */
+#define WC_TILT_MODE "FM1\r" /* enable extra protocol for tilt management */
+#define WC_NO_INCREMENT "IN0\r" /* do not enable increment mode */
+#define WC_STREAM_MODE "SR\r" /* enable continuous mode */
+#define WC_PRESSURE_MODE "PH1\r" /* enable pressure mode */
+#define WC_ZFILTER "ZF1\r" /* stop sending coordinates */
+#define WC_STOP "\nSP\r" /* stop sending coordinates */
+#define WC_START "ST\r" /* start sending coordinates */
+#define WC_NEW_RESOLUTION "NR" /* change the resolution */
+
+static const char * setup_string = WC_MULTI WC_UPPER_ORIGIN
+ WC_ALL_MACRO WC_NO_MACRO1 WC_RATE WC_NO_INCREMENT WC_STREAM_MODE WC_ZFILTER;
+
+static const char * penpartner_setup_string = WC_PRESSURE_MODE WC_START;
+
+#define WC_V_SINGLE "MT0\r"
+#define WC_V_MULTI "MT1\r"
+#define WC_V_ID "ID1\r"
+#define WC_V_19200 "BA19\r"
+#define WC_V_38400 "BA38\r"
+/* #define WC_V_9600 "BA96\r" */
+#define WC_V_9600 "$\r"
+
+#define WC_RESET_19200 "\r$" /* reset to 9600 baud */
+#define WC_RESET_19200_IV "\r#"
+
+static const char * intuos_setup_string = WC_V_MULTI WC_V_ID WC_RATE WC_START;
+
+#define COMMAND_SET_MASK 0xc0
+#define BAUD_RATE_MASK 0x0a
+#define PARITY_MASK 0x30
+#define DATA_LENGTH_MASK 0x40
+#define STOP_BIT_MASK 0x80
+
+#define HEADER_BIT 0x80
+#define ZAXIS_SIGN_BIT 0x40
+#define ZAXIS_BIT 0x04
+#define ZAXIS_BITS 0x3f
+#define POINTER_BIT 0x20
+#define PROXIMITY_BIT 0x40
+#define BUTTON_FLAG 0x08
+#define BUTTONS_BITS 0x78
+#define TILT_SIGN_BIT 0x40
+#define TILT_BITS 0x3f
+
+/* defines to discriminate second side button and the eraser */
+#define ERASER_PROX 4
+#define OTHER_PROX 1
+
+#define HANDLE_TILT(comm) ((comm)->wcmPktLength == 9)
+
+#define mils(res) (res * 100 / 2.54) /* resolution */
+
+/******************************************************************************
+ * Function/Macro keys variables
+ *****************************************************************************/
+static KeySym wacom_map[] =
+{
+ NoSymbol, /* 0x00 */
+ NoSymbol, /* 0x01 */
+ NoSymbol, /* 0x02 */
+ NoSymbol, /* 0x03 */
+ NoSymbol, /* 0x04 */
+ NoSymbol, /* 0x05 */
+ NoSymbol, /* 0x06 */
+ NoSymbol, /* 0x07 */
+ XK_F1, /* 0x08 */
+ XK_F2, /* 0x09 */
+ XK_F3, /* 0x0a */
+ XK_F4, /* 0x0b */
+ XK_F5, /* 0x0c */
+ XK_F6, /* 0x0d */
+ XK_F7, /* 0x0e */
+ XK_F8, /* 0x0f */
+ XK_F8, /* 0x10 */
+ XK_F10, /* 0x11 */
+ XK_F11, /* 0x12 */
+ XK_F12, /* 0x13 */
+ XK_F13, /* 0x14 */
+ XK_F14, /* 0x15 */
+ XK_F15, /* 0x16 */
+ XK_F16, /* 0x17 */
+ XK_F17, /* 0x18 */
+ XK_F18, /* 0x19 */
+ XK_F19, /* 0x1a */
+ XK_F20, /* 0x1b */
+ XK_F21, /* 0x1c */
+ XK_F22, /* 0x1d */
+ XK_F23, /* 0x1e */
+ XK_F24, /* 0x1f */
+ XK_F25, /* 0x20 */
+ XK_F26, /* 0x21 */
+ XK_F27, /* 0x22 */
+ XK_F28, /* 0x23 */
+ XK_F29, /* 0x24 */
+ XK_F30, /* 0x25 */
+ XK_F31, /* 0x26 */
+ XK_F32 /* 0x27 */
+};
+
+/* minKeyCode = 8 because this is the min legal key code */
+static KeySymsRec wacom_keysyms = {
+ /* map minKeyCode maxKC width */
+ wacom_map, 8, 0x27, 1
+};
+
+/******************************************************************************
+ * external declarations
+ *****************************************************************************/
+
+#ifdef LINUX_INPUT
+static void xf86WcmReadUSBInput(LocalDevicePtr);
+static Bool xf86WcmUSBOpen(LocalDevicePtr);
+#endif
+
+
+#ifndef XFREE86_V4
+
+#if defined(sun) && !defined(i386)
+#define ENQUEUE suneqEnqueue
+#else
+#define ENQUEUE xf86eqEnqueue
+
+extern void xf86eqEnqueue(
+#if NeedFunctionPrototypes
+ xEventPtr /*e*/
+#endif
+);
+#endif
+
+extern void miPointerDeltaCursor(
+#if NeedFunctionPrototypes
+ int /*dx*/,
+ int /*dy*/,
+ unsigned long /*time*/
+#endif
+);
+
+#endif /* pre 3.9 declarations */
+
+#if NeedFunctionPrototypes
+static LocalDevicePtr xf86WcmAllocateStylus(void);
+static LocalDevicePtr xf86WcmAllocateCursor(void);
+static LocalDevicePtr xf86WcmAllocateEraser(void);
+#endif
+
+#ifndef XFREE86_V4
+/*
+ ***************************************************************************
+ *
+ * xf86WcmConfig --
+ * Configure the device.
+ *
+ ***************************************************************************
+ */
+static Bool
+xf86WcmConfig(LocalDevicePtr *array,
+ int inx,
+ int max,
+ LexPtr val)
+{
+ LocalDevicePtr dev = array[inx];
+ WacomDevicePtr priv = (WacomDevicePtr)(dev->private);
+ WacomCommonPtr common = priv->common;
+ int token;
+ int mtoken;
+
+ DBG(1, ErrorF("xf86WcmConfig\n"));
+
+ if (xf86GetToken(WcmTab) != PORT) {
+ xf86ConfigError("PORT option must be the first option of a Wacom SubSection");
+ }
+
+ if (xf86GetToken(NULL) != STRING)
+ xf86ConfigError("Option string expected");
+ else {
+ int loop;
+
+ /* try to find another wacom device which share the same port */
+ for(loop=0; loop<max; loop++) {
+ if (loop == inx)
+ continue;
+ if ((array[loop]->device_config == xf86WcmConfig) &&
+ (strcmp(((WacomDevicePtr)array[loop]->private)->common->wcmDevice, val->str) == 0)) {
+ DBG(2, ErrorF("xf86WcmConfig wacom port share between"
+ " %s and %s\n",
+ dev->name, array[loop]->name));
+ ((WacomDevicePtr) array[loop]->private)->common->wcmHasEraser |= common->wcmHasEraser;
+ xfree(common->wcmDevices);
+ xfree(common);
+ common = priv->common = ((WacomDevicePtr) array[loop]->private)->common;
+ common->wcmNumDevices++;
+ common->wcmDevices = (LocalDevicePtr *) xrealloc(common->wcmDevices,
+ sizeof(LocalDevicePtr) * common->wcmNumDevices);
+ common->wcmDevices[common->wcmNumDevices - 1] = dev;
+ break;
+ }
+ }
+ if (loop == max) {
+ common->wcmDevice = strdup(val->str);
+ if (xf86Verbose)
+ ErrorF("%s Wacom port is %s\n", XCONFIG_GIVEN,
+ common->wcmDevice);
+ }
+ }
+
+ while ((token = xf86GetToken(WcmTab)) != ENDSUBSECTION) {
+ switch(token) {
+ case DEVICENAME:
+ if (xf86GetToken(NULL) != STRING)
+ xf86ConfigError("Option string expected");
+ dev->name = strdup(val->str);
+ if (xf86Verbose)
+ ErrorF("%s Wacom X device name is %s\n", XCONFIG_GIVEN,
+ dev->name);
+ break;
+
+ case THE_MODE:
+ mtoken = xf86GetToken(ModeTabRec);
+ if ((mtoken == EOF) || (mtoken == STRING) || (mtoken == NUMBER))
+ xf86ConfigError("Mode type token expected");
+ else {
+ switch (mtoken) {
+ case ABSOLUTE:
+ priv->flags = priv->flags | ABSOLUTE_FLAG;
+ break;
+ case RELATIVE:
+ priv->flags = priv->flags & ~ABSOLUTE_FLAG;
+ break;
+ default:
+ xf86ConfigError("Illegal Mode type");
+ break;
+ }
+ }
+ break;
+
+ case SUPPRESS:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmSuppress = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom suppress value is %d\n", XCONFIG_GIVEN,
+ common->wcmSuppress);
+ break;
+
+ case DEBUG_LEVEL:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ debug_level = val->num;
+ if (xf86Verbose) {
+#if DEBUG
+ ErrorF("%s Wacom debug level sets to %d\n", XCONFIG_GIVEN,
+ debug_level);
+#else
+ ErrorF("%s Wacom debug level not sets to %d because"
+ " debugging is not compiled\n", XCONFIG_GIVEN,
+ debug_level);
+#endif
+ }
+ break;
+
+ case TILT_MODE:
+ common->wcmFlags |= TILT_FLAG;
+ break;
+
+ case HISTORY_SIZE:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ dev->history_size = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom Motion history size is %d\n", XCONFIG_GIVEN,
+ dev->history_size);
+ break;
+
+ case ALWAYS_CORE:
+ xf86AlwaysCore(dev, TRUE);
+ if (xf86Verbose)
+ ErrorF("%s Wacom device always stays core pointer\n",
+ XCONFIG_GIVEN);
+ break;
+
+ case KEEP_SHAPE:
+ priv->flags |= KEEP_SHAPE_FLAG;
+ if (xf86Verbose)
+ ErrorF("%s Wacom keeps shape\n",
+ XCONFIG_GIVEN);
+ break;
+
+ case TOP_X:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ priv->topX = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom top x = %d\n", XCONFIG_GIVEN, priv->topX);
+ break;
+
+ case TOP_Y:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ priv->topY = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom top y = %d\n", XCONFIG_GIVEN, priv->topY);
+ break;
+
+ case BOTTOM_X:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ priv->bottomX = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom bottom x = %d\n", XCONFIG_GIVEN, priv->bottomX);
+ break;
+
+ case BOTTOM_Y:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ priv->bottomY = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom bottom y = %d\n", XCONFIG_GIVEN, priv->bottomY);
+ break;
+
+ case SERIAL:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ priv->serial = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom serial number = %u\n", XCONFIG_GIVEN,
+ priv->serial);
+ break;
+
+ case BAUD_RATE:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ switch(val->num) {
+ case 38400:
+ common->wcmLinkSpeed = 38400;
+ break;
+ case 19200:
+ common->wcmLinkSpeed = 19200;
+ break;
+ case 9600:
+ common->wcmLinkSpeed = 9600;
+ break;
+ default:
+ xf86ConfigError("Illegal speed value");
+ break;
+ }
+ if (xf86Verbose)
+ ErrorF("%s Wacom baud rate of %u\n", XCONFIG_GIVEN,
+ val->num);
+ break;
+
+ case THRESHOLD:
+ if (xf86GetToken(NULL) != STRING)
+ xf86ConfigError("Option string expected");
+
+ common->wcmThreshold = atoi(val->str);
+
+ if (xf86Verbose)
+ ErrorF("%s Wacom pressure threshold for button 1 = %d\n",
+ XCONFIG_GIVEN, common->wcmThreshold);
+ break;
+
+ case MAX_X:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmMaxX = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom max x = %d\n", XCONFIG_GIVEN, common->wcmMaxX);
+ break;
+
+ case MAX_Y:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmMaxY = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom max y = %d\n", XCONFIG_GIVEN, common->wcmMaxY);
+ break;
+
+ case MAX_Z:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmMaxZ = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom max y = %d\n", XCONFIG_GIVEN, common->wcmMaxZ);
+ break;
+
+ case RESOLUTION_X:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmResolX = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom resolution x = %d\n", XCONFIG_GIVEN, common->wcmResolX);
+ break;
+
+ case RESOLUTION_Y:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmResolY = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom resolution y = %d\n", XCONFIG_GIVEN, common->wcmResolY);
+ break;
+
+ case RESOLUTION_Z:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ common->wcmResolZ = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom resolution y = %d\n", XCONFIG_GIVEN, common->wcmResolZ);
+ break;
+
+ case USB:
+#ifdef LINUX_INPUT
+ dev->read_input=xf86WcmReadUSBInput;
+ common->wcmOpen=xf86WcmUSBOpen;
+ ErrorF("%s Wacom reading USB link\n", XCONFIG_GIVEN);
+#else
+ ErrorF("The USB version of the driver isn't available for your platform\n");
+#endif
+ break;
+
+ case SCREEN_NO:
+ if (xf86GetToken(NULL) != NUMBER)
+ xf86ConfigError("Option number expected");
+ priv->screen_no = val->num;
+ if (xf86Verbose)
+ ErrorF("%s Wacom attached screen = %d\n", XCONFIG_GIVEN,
+ priv->screen_no);
+ break;
+
+ case EOF:
+ FatalError("Unexpected EOF (missing EndSubSection)");
+ break;
+
+ default:
+ xf86ConfigError("Wacom subsection keyword expected");
+ break;
+ }
+ }
+
+ DBG(1, ErrorF("xf86WcmConfig name=%s\n", common->wcmDevice));
+
+ return Success;
+}
+#endif /* Pre 3.9 stuff */
+
+#if 0
+/*
+ ***************************************************************************
+ *
+ * ascii_to_hexa --
+ *
+ ***************************************************************************
+ */
+/*
+ * transform two ascii hexa representation into an unsigned char
+ * most significant byte is the first one
+ */
+static unsigned char
+ascii_to_hexa(char buf[2])
+{
+ unsigned char uc;
+
+ if (buf[0] >= 'A') {
+ uc = buf[0] - 'A' + 10;
+ }
+ else {
+ uc = buf[0] - '0';
+ }
+ uc = uc << 4;
+ if (buf[1] >= 'A') {
+ uc += buf[1] - 'A' + 10;
+ }
+ else {
+ uc += buf[1] - '0';
+ }
+ return uc;
+}
+#endif
+
+#ifndef XFREE86_V4
+/*
+ ***************************************************************************
+ *
+ * set_serial_speed --
+ *
+ * Set speed of the serial port.
+ *
+ ***************************************************************************
+ */
+static int
+set_serial_speed(int fd,
+ int speed_code)
+{
+ struct termios termios_tty;
+ int err;
+
+#ifdef POSIX_TTY
+ SYSCALL(err = tcgetattr(fd, &termios_tty));
+
+ if (err == -1) {
+ ErrorF("Wacom tcgetattr error : %s\n", strerror(errno));
+ return !Success;
+ }
+ termios_tty.c_iflag = IXOFF;
+ termios_tty.c_oflag = 0;
+ termios_tty.c_cflag = speed_code|CS8|CREAD|CLOCAL;
+ termios_tty.c_lflag = 0;
+
+ termios_tty.c_cc[VINTR] = 0;
+ termios_tty.c_cc[VQUIT] = 0;
+ termios_tty.c_cc[VERASE] = 0;
+ termios_tty.c_cc[VEOF] = 0;
+#ifdef VWERASE
+ termios_tty.c_cc[VWERASE] = 0;
+#endif
+#ifdef VREPRINT
+ termios_tty.c_cc[VREPRINT] = 0;
+#endif
+ termios_tty.c_cc[VKILL] = 0;
+ termios_tty.c_cc[VEOF] = 0;
+ termios_tty.c_cc[VEOL] = 0;
+#ifdef VEOL2
+ termios_tty.c_cc[VEOL2] = 0;
+#endif
+ termios_tty.c_cc[VSUSP] = 0;
+#ifdef VDSUSP
+ termios_tty.c_cc[VDSUSP] = 0;
+#endif
+#ifdef VDISCARD
+ termios_tty.c_cc[VDISCARD] = 0;
+#endif
+#ifdef VLNEXT
+ termios_tty.c_cc[VLNEXT] = 0;
+#endif
+
+ /* minimum 1 character in one read call and timeout to 100 ms */
+ termios_tty.c_cc[VMIN] = 1;
+ termios_tty.c_cc[VTIME] = 10;
+
+ SYSCALL(err = tcsetattr(fd, TCSANOW, &termios_tty));
+ if (err == -1) {
+ ErrorF("Wacom tcsetattr TCSANOW error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+#else
+ Code for OSs without POSIX tty functions
+#endif
+
+ return Success;
+}
+
+/*
+ ***************************************************************************
+ *
+ * wait_for_fd --
+ *
+ * Wait one second that the file descriptor becomes readable.
+ *
+ ***************************************************************************
+ */
+static int
+wait_for_fd(int fd)
+{
+ int err;
+ fd_set readfds;
+ struct timeval timeout;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ SYSCALL(err = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout));
+
+ return err;
+}
+
+/*
+ ***************************************************************************
+ *
+ * flush_input_fd --
+ *
+ * Flush all input pending on the file descriptor.
+ *
+ ***************************************************************************
+ */
+static int
+flush_input_fd(int fd)
+{
+ int err;
+ fd_set readfds;
+ struct timeval timeout;
+ char dummy[1];
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ do {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ SYSCALL(err = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout));
+
+ if (err > 0) {
+ SYSCALL(err = read(fd, &dummy, 1));
+ DBG(10, ErrorF("flush_input_fd: read %d bytes\n", err));
+ }
+ } while (err > 0);
+ return err;
+}
+#endif /* Pre 3.9 stuff */
+
+/*
+ ***************************************************************************
+ *
+ * send_request --
+ *
+ ***************************************************************************
+ */
+/*
+ * send a request and wait for the answer.
+ * the answer must begin with the first two chars of the request and must end
+ * with \r. The last character in the answer string (\r) is replaced by a \0.
+ */
+static char *
+send_request(int fd,
+ char *request,
+ char *answer)
+{
+ int len, nr;
+ int maxtry = MAXTRY;
+
+ /* send request string */
+ do {
+ SYSCALL(len = write(fd, request, strlen(request)));
+ if ((len == -1) && (errno != EAGAIN)) {
+ ErrorF("Wacom write error : %s", strerror(errno));
+ return NULL;
+ }
+ maxtry--;
+ } while ((len == -1) && maxtry);
+
+ if (maxtry == 0) {
+ ErrorF("Wacom unable to write request string '%s' after %d tries\n", request, MAXTRY);
+ return NULL;
+ }
+
+ do {
+ maxtry = MAXTRY;
+
+ /* Read the first byte of the answer which must be equal to the first
+ * byte of the request.
+ */
+ do {
+ if ((nr = wait_for_fd(fd)) > 0) {
+ SYSCALL(nr = read(fd, answer, 1));
+ if ((nr == -1) && (errno != EAGAIN)) {
+ ErrorF("Wacom read error : %s\n", strerror(errno));
+ return NULL;
+ }
+ DBG(10, ErrorF("%c err=%d [0]\n", answer[0], nr));
+ }
+ maxtry--;
+ } while ((answer[0] != request[0]) && maxtry);
+
+ if (maxtry == 0) {
+ ErrorF("Wacom unable to read first byte of request '%c%c' answer after %d tries\n",
+ request[0], request[1], MAXTRY);
+ return NULL;
+ }
+
+ /* Read the second byte of the answer which must be equal to the second
+ * byte of the request.
+ */
+ do {
+ maxtry = MAXTRY;
+ do {
+ if ((nr = wait_for_fd(fd)) > 0) {
+ SYSCALL(nr = read(fd, answer+1, 1));
+ if ((nr == -1) && (errno != EAGAIN)) {
+ ErrorF("Wacom read error : %s\n", strerror(errno));
+ return NULL;
+ }
+ DBG(10, ErrorF("%c err=%d [1]\n", answer[1], nr));
+ }
+ maxtry--;
+ } while ((nr <= 0) && maxtry);
+
+ if (maxtry == 0) {
+ ErrorF("Wacom unable to read second byte of request '%c%c' answer after %d tries\n",
+ request[0], request[1], MAXTRY);
+ return NULL;
+ }
+
+ if (answer[1] != request[1])
+ answer[0] = answer[1];
+
+ } while ((answer[0] == request[0]) &&
+ (answer[1] != request[1]));
+
+ } while ((answer[0] != request[0]) &&
+ (answer[1] != request[1]));
+
+ /* Read until carriage return or timeout (to handle broken protocol
+ * implementations which don't end with a <cr>).
+ */
+ len = 2;
+ maxtry = MAXTRY;
+ do {
+ do {
+ if ((nr = wait_for_fd(fd)) > 0) {
+ SYSCALL(nr = read(fd, answer+len, 1));
+ if ((nr == -1) && (errno != EAGAIN)) {
+ ErrorF("Wacom read error : %s\n", strerror(errno));
+ return NULL;
+ }
+ DBG(10, ErrorF("%c err=%d [%d]\n", answer[len], nr, len));
+ }
+ else {
+ DBG(10, ErrorF("timeout remains %d tries\n", maxtry));
+ maxtry--;
+ }
+ } while ((nr <= 0) && maxtry);
+
+ if (nr > 0) {
+ len += nr;
+ }
+
+ if (maxtry == 0) {
+ ErrorF("Wacom unable to read last byte of request '%c%c' answer after %d tries\n",
+ request[0], request[1], MAXTRY);
+ break;
+ }
+ } while (answer[len-1] != '\r');
+
+ if (len <= 3)
+ return NULL;
+
+ answer[len-1] = '\0';
+
+ return answer;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmConvert --
+ * Convert valuators to X and Y.
+ *
+ ***************************************************************************
+ */
+static Bool
+xf86WcmConvert(LocalDevicePtr local,
+ int first,
+ int num,
+ int v0,
+ int v1,
+ int v2,
+ int v3,
+ int v4,
+ int v5,
+ int* x,
+ int* y)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) local->private;
+
+ DBG(6, ErrorF("xf86WcmConvert\n"));
+
+ if (first != 0 || num == 1)
+ return FALSE;
+
+#ifdef XFREE86_V4
+ {
+ ScreenPtr pscr;
+
+ if (priv->screen_no != -1) {
+ pscr = screenInfo.screens[priv->screen_no];
+ } else {
+ pscr = miPointerCurrentScreen();
+ }
+
+ if (pscr == NULL)
+ return FALSE;
+
+ priv->factorX = ((double) pscr->width)
+ / (priv->bottomX - priv->topX);
+ priv->factorY = ((double) pscr->height)
+ / (priv->bottomY - priv->topY);
+ }
+#endif
+
+ *x = v0 * priv->factorX + 0.5;
+ *y = v1 * priv->factorY + 0.5;
+
+ DBG(6, ErrorF("Wacom converted v0=%d v1=%d to x=%d y=%d\n",
+ v0, v1, *x, *y));
+#ifdef XFREE86_V4
+ if (priv->screen_no != -1) {
+ xf86XInputSetScreen(local, priv->screen_no, *x, *y);
+ }
+#endif
+ return TRUE;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmReverseConvert --
+ * Convert X and Y to valuators.
+ *
+ ***************************************************************************
+ */
+static Bool
+xf86WcmReverseConvert(LocalDevicePtr local,
+ int x,
+ int y,
+ int *valuators)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) local->private;
+
+#ifdef XFREE86_V4
+ priv->factorX = ((double) miPointerCurrentScreen()->width)
+ / (priv->bottomX - priv->topX);
+ priv->factorY = ((double) miPointerCurrentScreen()->height)
+ / (priv->bottomY - priv->topY);
+#endif
+
+ valuators[0] = x / priv->factorX + 0.5;
+ valuators[1] = y / priv->factorY + 0.5;
+
+ DBG(6, ErrorF("Wacom converted x=%d y=%d to v0=%d v1=%d\n", x, y,
+ valuators[0], valuators[1]));
+
+ return TRUE;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmSendButtons --
+ * Send button events by comparing the current button mask with the
+ * previous one.
+ *
+ ***************************************************************************
+ */
+static void
+xf86WcmSendButtons(LocalDevicePtr local,
+ int buttons,
+ int rx,
+ int ry,
+ int rz,
+ int rtx,
+ int rty,
+ int rwheel)
+
+{
+ int button;
+ WacomDevicePtr priv = (WacomDevicePtr) local->private;
+
+ for (button=1; button<=16; button++) {
+ int mask = 1 << (button-1);
+
+ if ((mask & priv->oldButtons) != (mask & buttons)) {
+ DBG(4, ErrorF("xf86WcmSendButtons button=%d state=%d\n",
+ button, (buttons & mask) != 0));
+ xf86PostButtonEvent(local->dev,
+ (priv->flags & ABSOLUTE_FLAG),
+ button, (buttons & mask) != 0,
+ 0, 6, rx, ry, rz, rtx, rty, rwheel);
+ }
+ }
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmSendEvents --
+ * Send events according to the device state.
+ *
+ ***************************************************************************
+ */
+static void
+xf86WcmSendEvents(LocalDevicePtr local,
+ int type,
+ unsigned int serial,
+ int is_stylus,
+ int is_button,
+ int is_proximity,
+ int x,
+ int y,
+ int z,
+ int buttons,
+ int tx,
+ int ty,
+ int wheel)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) local->private;
+ WacomCommonPtr common = priv->common;
+ int rx, ry, rz, rtx, rty, rwheel;
+ int is_core_pointer, is_absolute;
+
+ if ((DEVICE_ID(priv->flags) != type) ||
+ ((common->wcmProtocolLevel == 5) &&
+ priv->serial && (serial != priv->serial))) {
+ DBG(7,
+ {if (common->wcmProtocolLevel == 5) {
+ ErrorF("xf86WcmSendEvents not the same device id (%u,%u)\n",
+ serial, priv->serial);
+ } else {
+ ErrorF("xf86WcmSendEvents not the same device type (%u,%u)\n",
+ DEVICE_ID(priv->flags), type);}});
+ return;
+ }
+
+ DBG(7, ErrorF("[%s] prox=%s\tx=%d\ty=%d\tz=%d\tbutton=%s\tbuttons=%d\ttx=%d ty=%d\twl=%d\n",
+ (type == STYLUS_ID) ? "stylus" : (type == CURSOR_ID) ? "cursor" : "eraser",
+ is_proximity ? "true" : "false",
+ x, y, z,
+ is_button ? "true" : "false", buttons,
+ tx, ty, wheel));
+
+ /* Translate coordinates according to Top and Bottom points
+ * if we are outside the zone do as a ProximityOut event.
+ */
+
+ if (x > priv->bottomX) {
+ is_proximity = FALSE;
+ buttons = 0;
+ x = priv->bottomX;
+ }
+
+ if (y > priv->bottomY) {
+ is_proximity = FALSE;
+ buttons = 0;
+ y = priv->bottomY;
+ }
+
+ DBG(10, ErrorF("topX=%d topY=%d\n", priv->topX, priv->topY));
+
+ x = x - priv->topX;
+ y = y - priv->topY;
+
+ if (x < 0) {
+ is_proximity = FALSE;
+ buttons = 0;
+ x = 0;
+ }
+
+ if (y < 0) {
+ is_proximity = FALSE;
+ buttons = 0;
+ y = 0;
+ }
+
+ is_absolute = (priv->flags & ABSOLUTE_FLAG);
+ is_core_pointer = xf86IsCorePointer(local->dev);
+
+ DBG(6, ErrorF("[%s] %s prox=%s\tx=%d\ty=%d\tz=%d\tbutton=%s\tbuttons=%d\n",
+ is_stylus ? "stylus" : "cursor",
+ is_absolute ? "abs" : "rel",
+ is_proximity ? "true" : "false",
+ x, y, z,
+ is_button ? "true" : "false", buttons));
+
+ /* Hardware filtering isn't working on Graphire so we do it here.
+ */
+ if ((common->wcmFlags & GRAPHIRE_FLAG) &&
+ ((is_proximity && priv->oldProximity) ||
+ ((is_proximity == 0) && (priv->oldProximity == 0))) &&
+ (buttons == priv->oldButtons) &&
+ (ABS(x - priv->oldX) <= common->wcmSuppress) &&
+ (ABS(y - priv->oldY) <= common->wcmSuppress) &&
+ (ABS(z - priv->oldZ) < 3) &&
+ (ABS(tx - priv->oldTiltX) < 3) &&
+ (ABS(ty - priv->oldTiltY) < 3)) {
+
+ DBG(10, ErrorF("Graphire filtered\n"));
+
+ return;
+ }
+
+ /* sets rx and ry according to the mode */
+ if (is_absolute) {
+ rx = x;
+ ry = y;
+ rz = z;
+ rtx = tx;
+ rty = ty;
+ rwheel = wheel;
+ } else {
+ rx = x - priv->oldX;
+ ry = y - priv->oldY;
+ rz = z - priv->oldZ;
+ rtx = tx - priv->oldTiltX;
+ rty = ty - priv->oldTiltY;
+ rwheel = wheel - priv->oldWheel;
+ }
+
+ /* coordinates are ready we can send events */
+ if (is_proximity) {
+
+ if (!priv->oldProximity) {
+ xf86PostProximityEvent(local->dev, 1, 0, 6, rx, ry, z, tx, ty, rwheel);
+
+ priv->flags |= FIRST_TOUCH_FLAG;
+ DBG(4, ErrorF("xf86WcmSendEvents FIRST_TOUCH_FLAG set\n"));
+
+ if (common->wcmProtocolLevel == 4) {
+ /* handle the two sides switches in the stylus */
+ if (is_stylus && (buttons == 4)) {
+ priv->oldProximity = ERASER_PROX;
+ }
+ else {
+ priv->oldProximity = OTHER_PROX;
+ }
+ }
+ else {
+ priv->oldProximity = OTHER_PROX;
+ }
+ }
+
+ if (common->wcmProtocolLevel == 4 &&
+ !(common->wcmFlags & GRAPHIRE_FLAG)) {
+ /* The stylus reports button 4 for the second side
+ * switch and button 4/5 for the eraser tip. We know
+ * how to choose when we come in proximity for the
+ * first time. If we are in proximity and button 4 then
+ * we have the eraser else we have the second side
+ * switch.
+ */
+ if (is_stylus) {
+ if (buttons == 4) {
+ buttons = (priv->oldProximity == ERASER_PROX) ? 0 : 3;
+ }
+ else {
+ if (priv->oldProximity == ERASER_PROX && buttons == 5) {
+ buttons = ((DEVICE_ID(priv->flags) == ERASER_ID) ? 1 : 4);
+ }
+ }
+ }
+ else {
+ /* If the button flag is pressed, but the switch state
+ * is zero, this means that cursor button 16 was pressed
+ */
+ if (is_button && buttons == 0) {
+ buttons = 16;
+ }
+ }
+ }
+ DBG(4, ErrorF("xf86WcmSendEvents %s rx=%d ry=%d rz=%d buttons=%d\n",
+ is_stylus ? "stylus" : "cursor", rx, ry, rz, buttons));
+
+ /* Turn button index reported into a bit mask for WACOM IV.
+ * The WACOM V and Graphire models already report buttons
+ * as a bit mask.
+ */
+ if (common->wcmProtocolLevel == 4 &&
+ !(common->wcmFlags & GRAPHIRE_FLAG)) {
+ buttons = 1 << (buttons - 1);
+ }
+
+ if ((priv->oldX != x) ||
+ (priv->oldY != y) ||
+ (priv->oldZ != z) ||
+ (is_stylus && HANDLE_TILT(common) &&
+ (tx != priv->oldTiltX || ty != priv->oldTiltY))) {
+ if (!is_absolute && (priv->flags & FIRST_TOUCH_FLAG)) {
+ priv->flags -= FIRST_TOUCH_FLAG;
+ DBG(4, ErrorF("xf86WcmSendEvents FIRST_TOUCH_FLAG unset\n"));
+ } else {
+ xf86PostMotionEvent(local->dev, is_absolute, 0, 6, rx, ry, rz,
+ rtx, rty, rwheel);
+ }
+ }
+
+ if (priv->oldButtons != buttons) {
+ xf86WcmSendButtons (local, buttons, rx, ry, rz, rtx, rty, rwheel);
+ }
+
+ /* Simulate buttons 4 and 5 for Graphire wheel */
+ if ((((common->wcmProtocolLevel == 4) && (common->wcmFlags & GRAPHIRE_FLAG) && (wheel != 0)) ||
+ ((common->wcmOpen == xf86WcmUSBOpen) && (wheel != priv->oldWheel))) &&
+ !is_stylus) {
+ int fake_button;
+
+ if (common->wcmOpen == xf86WcmUSBOpen) {
+ fake_button = (wheel > priv->oldWheel) ? 5 : 4;
+ } else{
+ fake_button = (wheel > 0) ? 5 : 4;
+ }
+
+ xf86PostButtonEvent(local->dev,
+ (priv->flags & ABSOLUTE_FLAG),
+ fake_button, 1,
+ 0, 6, rx, ry, rz, rtx, rty, rwheel);
+
+ xf86PostButtonEvent(local->dev,
+ (priv->flags & ABSOLUTE_FLAG),
+ fake_button, 0,
+ 0, 6, rx, ry, rz, rtx, rty, rwheel);
+ }
+
+ priv->oldButtons = buttons;
+ priv->oldX = x;
+ priv->oldY = y;
+ priv->oldZ = z;
+ priv->oldTiltX = tx;
+ priv->oldTiltY = ty;
+ priv->oldWheel = wheel;
+ }
+ else { /* !PROXIMITY */
+ /* reports button up when the device has been down and becomes out of proximity */
+ if (priv->oldButtons) {
+ xf86WcmSendButtons (local, 0, rx, ry, rz, rtx, rty, rwheel);
+ priv->oldButtons = 0;
+ }
+ if (!is_core_pointer) {
+ /* macro button management */
+ if (common->wcmProtocolLevel == 4 && buttons) {
+ int macro = z / 2;
+
+ DBG(6, ErrorF("macro=%d buttons=%d wacom_map[%d]=%x\n",
+ macro, buttons, macro, wacom_map[macro]));
+
+ /* First available Keycode begins at 8 => macro+7 */
+ xf86PostKeyEvent(local->dev, macro+7, 1,
+ is_absolute, 0, 6,
+ 0, 0, buttons, rtx, rty, rwheel);
+ xf86PostKeyEvent(local->dev, macro+7, 0,
+ is_absolute, 0, 6,
+ 0, 0, buttons, rtx, rty, rwheel);
+ }
+ if (priv->oldProximity) {
+ xf86PostProximityEvent(local->dev, 0, 0, 6, rx, ry, rz,
+ rtx, rty, rwheel);
+ }
+ }
+ priv->oldProximity = 0;
+ }
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmSuppress --
+ * Determine whether device state has changed enough - return 1
+ * if not.
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmSuppress(int suppress,
+ WacomDeviceState *ds1,
+ WacomDeviceState *ds2)
+{
+ if (ds1->buttons != ds2->buttons) return 0;
+ if (ds1->proximity != ds2->proximity) return 0;
+ if (ABS(ds1->x - ds2->x) >= suppress) return 0;
+ if (ABS(ds1->y - ds2->y) >= suppress) return 0;
+ if (ABS(ds1->pressure - ds2->pressure) >= suppress) return 0;
+ if ((1800 + ds1->rotation - ds2->rotation) % 1800 >= suppress &&
+ (1800 + ds2->rotation - ds1->rotation) % 1800 >= suppress) return 0;
+ if (ABS(ds1->wheel - ds2->wheel) >= suppress) return 0;
+ return 1;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmIntuosFilter --
+ * Correct some hardware defects we've been seeing in Intuos pads,
+ * but also cuts down quite a bit on jitter.
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmIntuosFilter(WacomFilterState *state,
+ int coord,
+ int tilt)
+{
+ int tilt_filtered;
+ int ts;
+ int x0_pred;
+ int x0_pred1;
+ int x0, x1, x2, x3;
+ int x;
+
+ tilt_filtered = tilt + state->tilt[1] + state->tilt[2] + state->tilt[3];
+ state->tilt[2] = state->tilt[1];
+ state->tilt[1] = state->tilt[0];
+ state->tilt[0] = tilt;
+
+ x0 = coord;
+ x1 = state->coord[0];
+ x2 = state->coord[1];
+ x3 = state->coord[2];
+ state->coord[0] = x0;
+ state->coord[1] = x1;
+ state->coord[2] = x2;
+
+ ts = tilt_filtered >= 0 ? 1 : -1;
+
+ if (state->state == 0 || state->state == 3) {
+ x0_pred = 2 * x1 - x2;
+ x0_pred1 = 3 * x2 - 2 * x3;
+ if (ts * (x0 - x0_pred) > 12 &&
+ ts * (x0 - x0_pred1) > 12) {
+ /* detected a jump at x0 */
+ state->state = 1;
+ x = x1;
+ }
+ else if (state->state == 0) {
+ x = (7 * x0 + 14 * x1 + 15 * x2 - 4 * x3 + 16) >> 5;
+ }
+ else { /* state->state == 3 */
+ /* a jump at x3 was detected */
+ x = (x0 + 2 * x1 + x2 + 2) >> 2;
+ state->state = 0;
+ }
+ }
+ else if (state->state == 1) {
+ /* a jump at x1 was detected */
+ x = (3 * x0 + 7 * x2 - 2 * x3 + 4) >> 3;
+ state->state = 2;
+ }
+ else { /* state->state == 2 */
+ /* a jump at x2 was detected */
+ x = x1;
+ state->state = 3;
+ }
+
+ return x;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmReadInput --
+ * Read the new events from the device, and enqueue them.
+ *
+ ***************************************************************************
+ */
+static void
+xf86WcmReadInput(LocalDevicePtr local)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) local->private;
+ WacomCommonPtr common = priv->common;
+ int len, loop, idx;
+ int is_stylus = 1, is_button, is_proximity, wheel=0;
+ int is_absolute = (priv->flags & ABSOLUTE_FLAG);
+ int x, y, z, buttons, tx = 0, ty = 0;
+ unsigned char buffer[BUFFER_SIZE];
+ WacomDeviceState *ds;
+ WacomDeviceState old_ds;
+ int have_data;
+
+ DBG(7, ErrorF("xf86WcmReadInput BEGIN device=%s fd=%d\n",
+ common->wcmDevice, local->fd));
+
+ SYSCALL(len = read(local->fd, buffer, sizeof(buffer)));
+
+ if (len <= 0) {
+ ErrorF("Error reading wacom device : %s\n", strerror(errno));
+ return;
+ } else {
+ DBG(10, ErrorF("xf86WcmReadInput read %d bytes\n", len));
+ }
+
+ for(loop=0; loop<len; loop++) {
+
+ /* Format of 7 bytes data packet for Wacom Tablets
+ Byte 1
+ bit 7 Sync bit always 1
+ bit 6 Pointing device detected
+ bit 5 Cursor = 0 / Stylus = 1
+ bit 4 Reserved
+ bit 3 1 if a button on the pointing device has been pressed
+ bit 2 Reserved
+ bit 1 X15
+ bit 0 X14
+
+ Byte 2
+ bit 7 Always 0
+ bits 6-0 = X13 - X7
+
+ Byte 3
+ bit 7 Always 0
+ bits 6-0 = X6 - X0
+
+ Byte 4
+ bit 7 Always 0
+ bit 6 B3
+ bit 5 B2
+ bit 4 B1
+ bit 3 B0
+ bit 2 P0
+ bit 1 Y15
+ bit 0 Y14
+
+ Byte 5
+ bit 7 Always 0
+ bits 6-0 = Y13 - Y7
+
+ Byte 6
+ bit 7 Always 0
+ bits 6-0 = Y6 - Y0
+
+ Byte 7
+ bit 7 Always 0
+ bit 6 Sign of pressure data
+ bit 5 P6
+ bit 4 P5
+ bit 3 P4
+ bit 2 P3
+ bit 1 P2
+ bit 0 P1
+
+ byte 8 and 9 are optional and present only
+ in tilt mode.
+
+ Byte 8
+ bit 7 Always 0
+ bit 6 Sign of tilt X
+ bit 5 Xt6
+ bit 4 Xt5
+ bit 3 Xt4
+ bit 2 Xt3
+ bit 1 Xt2
+ bit 0 Xt1
+
+ Byte 9
+ bit 7 Always 0
+ bit 6 Sign of tilt Y
+ bit 5 Yt6
+ bit 4 Yt5
+ bit 3 Yt4
+ bit 2 Yt3
+ bit 1 Yt2
+ bit 0 Yt1
+
+ */
+
+ if ((common->wcmIndex == 0) && !(buffer[loop] & HEADER_BIT)) { /* magic bit is not OK */
+ DBG(6, ErrorF("xf86WcmReadInput bad magic number 0x%x (pktlength=%d) %d\n",
+ buffer[loop], common->wcmPktLength, loop));
+ continue;
+ }
+ else { /* magic bit at wrong place */
+ if ((common->wcmIndex != 0) && (buffer[loop] & HEADER_BIT)) {
+ DBG(6, ErrorF("xf86WcmReadInput magic number 0x%x detetected at index %d loop=%d\n",
+ (unsigned int) buffer[loop], common->wcmIndex, loop));
+ common->wcmIndex = 0;
+ }
+ }
+
+ common->wcmData[common->wcmIndex++] = buffer[loop];
+
+ if (common->wcmProtocolLevel == 4 &&
+ common->wcmIndex == common->wcmPktLength) {
+ int is_graphire = common->wcmFlags & GRAPHIRE_FLAG;
+
+ /* the packet is OK */
+
+ /* reset char count for next read */
+ common->wcmIndex = 0;
+
+ x = (((common->wcmData[0] & 0x3) << 14) +
+ (common->wcmData[1] << 7) +
+ common->wcmData[2]);
+ y = (((common->wcmData[3] & 0x3) << 14) +
+ (common->wcmData[4] << 7) +
+ common->wcmData[5]);
+
+ /* check which device we have */
+ is_stylus = (common->wcmData[0] & POINTER_BIT);
+
+ z = ((common->wcmData[6] & ZAXIS_BITS) * 2) +
+ ((common->wcmData[3] & ZAXIS_BIT) >> 2);
+
+ if (common->wcmMaxZ == 512) {
+ z = z*4 + ((common->wcmData[0] & ZAXIS_BIT) >> 1);
+
+ if (!(common->wcmData[6] & ZAXIS_SIGN_BIT)) {
+ z += 256;
+ }
+ DBG(10, ErrorF("graphire pressure(%c)=%d\n",
+ (common->wcmData[6] & ZAXIS_SIGN_BIT) ? '-' : '+', z));
+ }
+ else {
+ if (!(common->wcmData[6] & ZAXIS_SIGN_BIT)) {
+ z += (common->wcmMaxZ / 2);
+ }
+ }
+
+ is_proximity = (common->wcmData[0] & PROXIMITY_BIT);
+
+ if (is_graphire) {
+ if (is_stylus) {
+ buttons = ((common->wcmData[3] & 0x30) >> 3) |
+ (z >= common->wcmThreshold ? 1 : 0);
+ }
+ else {
+ buttons = (common->wcmData[3] & 0x38) >> 3;
+
+ wheel = (common->wcmData[6] & 0x30) >> 4;
+
+ if (common->wcmData[6] & 0x40) {
+ wheel = -wheel;
+ }
+ }
+ is_button = (buttons != 0);
+
+ DBG(10, ErrorF("graphire buttons=%d prox=%d wheel=%d\n", buttons, is_proximity, wheel));
+ }
+ else {
+ is_button = (common->wcmData[0] & BUTTON_FLAG);
+ buttons = (common->wcmData[3] & BUTTONS_BITS) >> 3;
+ }
+
+ /* The stylus reports button 4 for the second side
+ * switch and button 4/5 for the eraser tip. We know
+ * how to choose when we come in proximity for the
+ * first time. If we are in proximity and button 4 then
+ * we have the eraser else we have the second side
+ * switch.
+ */
+ if (is_stylus) {
+ if (!common->wcmStylusProximity && is_proximity) {
+ if (is_graphire) {
+ common->wcmStylusSide = !(common->wcmData[3] & 0x40);
+ }
+ else {
+ common->wcmStylusSide = (buttons != 4);
+ }
+ }
+ DBG(8, ErrorF("xf86WcmReadInput %s side\n",
+ common->wcmStylusSide ? "stylus" : "eraser"));
+ common->wcmStylusProximity = is_proximity;
+
+ /* handle tilt values only for stylus */
+ if (HANDLE_TILT(common)) {
+ tx = (common->wcmData[7] & TILT_BITS);
+ ty = (common->wcmData[8] & TILT_BITS);
+ if (common->wcmData[7] & TILT_SIGN_BIT)
+ tx -= (TILT_BITS + 1);
+ if (common->wcmData[8] & TILT_SIGN_BIT)
+ ty -= (TILT_BITS + 1);
+ }
+ }
+
+ for(idx=0; idx<common->wcmNumDevices; idx++) {
+ LocalDevicePtr local_dev = common->wcmDevices[idx];
+ WacomDevicePtr priv = (WacomDevicePtr) local_dev->private;
+ int temp_buttons = buttons;
+ int temp_is_proximity = is_proximity;
+ int curDevice;
+
+ DBG(7, ErrorF("xf86WcmReadInput trying to send to %s\n",
+ local_dev->name));
+
+ /* check for device type (STYLUS, ERASER or CURSOR) */
+
+ if (is_stylus) {
+ /*
+ * The eraser is reported as button 4 and 5 of the stylus.
+ * if we haven't an independent device for the eraser
+ * report the button as button 3 of the stylus.
+ */
+ if (is_proximity) {
+ if (is_graphire) {
+ if (common->wcmData[3] & 0x40) {
+ curDevice = ERASER_ID;
+ }
+ else {
+ curDevice = STYLUS_ID;
+ }
+ }
+ else {
+ if ((buttons & 4) && common->wcmHasEraser &&
+ ((!priv->oldProximity ||
+ (priv->oldProximity == ERASER_PROX)))) {
+ curDevice = ERASER_ID;
+ } else {
+ curDevice = STYLUS_ID;
+ }
+ }
+ } else {
+ /*
+ * When we are out of proximity with the eraser the
+ * button 4 isn't reported so we must check the
+ * previous proximity device.
+ */
+ if (common->wcmHasEraser && (priv->oldProximity == ERASER_PROX)) {
+ curDevice = ERASER_ID;
+ } else {
+ curDevice = STYLUS_ID;
+ }
+ }
+
+ /* We check here to see if we changed between eraser and stylus
+ * without leaving proximity. The most likely cause is that
+ * we were fooled by the second side switch into thinking the
+ * stylus was the eraser. If this happens, we send
+ * a proximity-out for the old device.
+ */
+ if ((DEVICE_ID(priv->flags) == STYLUS_ID ||
+ DEVICE_ID(priv->flags) == ERASER_ID) &&
+ curDevice != DEVICE_ID(priv->flags)) {
+ if (priv->oldProximity) {
+ curDevice = DEVICE_ID(priv->flags);
+ temp_buttons = 0;
+ temp_is_proximity = 0;
+ DBG(10, ErrorF("eraser and stylus mix\n"));
+ } else
+ continue;
+ }
+
+ DBG(10, ErrorF((DEVICE_ID(priv->flags) == ERASER_ID) ?
+ "Eraser\n" :
+ "Stylus\n"));
+ }
+ else {
+ if (DEVICE_ID(priv->flags) != CURSOR_ID)
+ continue;
+ DBG(10, ErrorF("Cursor\n"));
+ curDevice = CURSOR_ID;
+ }
+
+ xf86WcmSendEvents(common->wcmDevices[idx],
+ curDevice, 0,
+ is_stylus,
+ is_button,
+ temp_is_proximity,
+ x, y, z, temp_buttons,
+ tx, ty, wheel);
+ }
+ } /* protocol 4 */
+ else if (common->wcmProtocolLevel == 5 &&
+ common->wcmIndex == common->wcmPktLength) {
+ /* the packet is OK */
+ int x, y;
+
+ /* reset count for read of next packet */
+ common->wcmIndex = 0;
+
+ ds = &common->wcmDevStat[common->wcmData[0] & 0x01];
+ old_ds = *ds;
+ have_data = 0;
+
+ DBG(7, ErrorF("packet header = 0x%x\n",
+ (unsigned int)common->wcmData[0]));
+
+ /* Device ID packet */
+ if ((common->wcmData[0] & 0xfc) == 0xc0) {
+ memset(ds, 0, sizeof(*ds));
+ ds->proximity = 1;
+ ds->device_id = (((common->wcmData[1] & 0x7f) << 5) |
+ ((common->wcmData[2] & 0x7c) >> 2));
+ ds->serial_num = (((common->wcmData[2] & 0x03) << 30) |
+ ((common->wcmData[3] & 0x7f) << 23) |
+ ((common->wcmData[4] & 0x7f) << 16) |
+ ((common->wcmData[5] & 0x7f) << 9) |
+ ((common->wcmData[6] & 0x7f) << 23) |
+ ((common->wcmData[7] & 0x60) >> 5));
+ if ((ds->device_id & 0xf06) != 0x802)
+ ds->discard_first = 1;
+
+ if (PEN(ds) || STROKING_PEN(ds) || INKING_PEN(ds) || AIRBRUSH(ds))
+ ds->device_type = STYLUS_ID;
+ else if (MOUSE_4D(ds) || LENS_CURSOR(ds))
+ ds->device_type = CURSOR_ID;
+ else
+ ds->device_type = ERASER_ID;
+
+ DBG(6, ErrorF("device_id=0x%x serial_num=%u type=%s\n",
+ ds->device_id, ds->serial_num,
+ (ds->device_type == STYLUS_ID) ? "stylus"
+ : (ds->device_type == CURSOR_ID) ? "cursor"
+ : "eraser"));
+ }
+ /* Out of proximity packet */
+ else if ((common->wcmData[0] & 0xfe) == 0x80) {
+ ds->proximity = 0;
+ have_data = 1;
+ }
+ /* General pen packet or eraser packet or airbrush first packet */
+ else if (((common->wcmData[0] & 0xb8) == 0xa0) ||
+ /* airbrush second packet */
+ ((common->wcmData[0] & 0xbe) == 0xb4)) {
+ is_stylus = 1;
+ ds->x = (((common->wcmData[1] & 0x7f) << 9) |
+ ((common->wcmData[2] & 0x7f) << 2) |
+ ((common->wcmData[3] & 0x60) >> 5));
+ ds->y = (((common->wcmData[3] & 0x1f) << 11) |
+ ((common->wcmData[4] & 0x7f) << 4) |
+ ((common->wcmData[5] & 0x78) >> 3));
+ if ((common->wcmData[0] & 0xb8) == 0xa0) {
+ ds->pressure = (((common->wcmData[5] & 0x07) << 7) |
+ (common->wcmData[6] & 0x7f));
+ ds->buttons = (((common->wcmData[0]) & 0x06) |
+ (ds->pressure >= common->wcmThreshold));
+ }
+ else {
+ ds->wheel = (((common->wcmData[5] & 0x07) << 7) |
+ (common->wcmData[6] & 0x7f));
+ }
+ ds->tiltx = (common->wcmData[7] & TILT_BITS);
+ ds->tilty = (common->wcmData[8] & TILT_BITS);
+ if (common->wcmData[7] & TILT_SIGN_BIT)
+ ds->tiltx -= (TILT_BITS + 1);
+ if (common->wcmData[8] & TILT_SIGN_BIT)
+ ds->tilty -= (TILT_BITS + 1);
+ ds->proximity = (common->wcmData[0] & PROXIMITY_BIT);
+ have_data = 1;
+ }
+ /* 4D mouse 1st packet or Lens cursor packet */
+ else if (((common->wcmData[0] & 0xbe) == 0xa8) ||
+ ((common->wcmData[0] & 0xbe) == 0xb0)) {
+ is_stylus = 0;
+ ds->x = (((common->wcmData[1] & 0x7f) << 9) |
+ ((common->wcmData[2] & 0x7f) << 2) |
+ ((common->wcmData[3] & 0x60) >> 5));
+ ds->y = (((common->wcmData[3] & 0x1f) << 11) |
+ ((common->wcmData[4] & 0x7f) << 4) |
+ ((common->wcmData[5] & 0x78) >> 3));
+ ds->tilty = 0;
+ ds->wheel = (((common->wcmData[5] & 0x07) << 7) |
+ (common->wcmData[6] & 0x7f));
+ if (common->wcmData[8] & 0x08) ds->wheel = -ds->wheel;
+ /* 4D mouse */
+ if (MOUSE_4D(ds)) {
+ ds->buttons = (((common->wcmData[8] & 0x70) >> 1) |
+ (common->wcmData[8] & 0x07));
+ have_data = !ds->discard_first;
+ }
+ /* Lens cursor */
+ else {
+ ds->buttons = common->wcmData[8];
+ have_data = 1;
+ }
+ ds->proximity = (common->wcmData[0] & PROXIMITY_BIT);
+ }
+ /* 4D mouse 2nd packet */
+ else if ((common->wcmData[0] & 0xbe) == 0xaa) {
+ is_stylus = 0;
+ ds->x = (((common->wcmData[1] & 0x7f) << 9) |
+ ((common->wcmData[2] & 0x7f) << 2) |
+ ((common->wcmData[3] & 0x60) >> 5));
+ ds->y = (((common->wcmData[3] & 0x1f) << 11) |
+ ((common->wcmData[4] & 0x7f) << 4) |
+ ((common->wcmData[5] & 0x78) >> 3));
+ ds->tilty = 0;
+ ds->rotation = (((common->wcmData[6] & 0x0f) << 7) |
+ (common->wcmData[7] & 0x7f));
+ ds->tiltx = ((900 - ((ds->rotation + 900) % 1800)) >> 1);
+ ds->proximity = (common->wcmData[0] & PROXIMITY_BIT);
+ have_data = 1;
+ ds->discard_first = 0;
+ }
+ else {
+ DBG(10, ErrorF("unknown wacom V packet 0x%x\n",
+ common->wcmData[0]));
+ }
+
+ /* Suppress data */
+ if (have_data &&
+ xf86WcmSuppress(common->wcmSuppress, &old_ds, ds)) {
+ DBG(10, ErrorF("Suppressing data according to filter\n"));
+ *ds = old_ds;
+ have_data = 0;
+ }
+
+ if (have_data) {
+ if (is_absolute) {
+ x = xf86WcmIntuosFilter (&ds->x_filter, ds->x, ds->tiltx);
+ y = xf86WcmIntuosFilter (&ds->y_filter, ds->y, ds->tilty);
+ }
+ else {
+ x = ds->x;
+ y = ds->y;
+ }
+ for(idx=0; idx<common->wcmNumDevices; idx++) {
+ DBG(7, ErrorF("xf86WcmReadInput trying to send to %s\n",
+ common->wcmDevices[idx]->name));
+
+ xf86WcmSendEvents(common->wcmDevices[idx],
+ ds->device_type, ds->serial_num,
+ is_stylus,
+ ds->buttons,
+ ds->proximity,
+ x, y,
+ ds->pressure,
+ ds->buttons,
+ ds->tiltx, ds->tilty,
+ ds->wheel);
+ }
+ }
+ } /* protocol 5 */
+ } /* next data */
+ DBG(7, ErrorF("xf86WcmReadInput END local=0x%x priv=0x%x index=%d\n",
+ local, priv, common->wcmIndex));
+}
+
+#ifdef LINUX_INPUT
+/*
+ ***************************************************************************
+ *
+ * xf86WcmIsUSBLine --
+ * Test if the attached device is a USB one.
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmIsUSBLine(int fd)
+{
+ int version;
+ int err;
+
+ SYSCALL(err = ioctl(fd, EVIOCGVERSION, &version));
+
+ if (!err) {
+ ErrorF("%s Wacom Kernel Input driver version is %d.%d.%d\n", XCONFIG_PROBED,
+ version >> 16, (version >> 8) & 0xff, version & 0xff);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int ThrottleToRate(int x)
+{
+ if (x<0) x=-x;
+
+ /* piece-wise exponential function */
+
+ if (x < 128) return 0; /* infinite */
+ if (x < 256) return 1000; /* 1 second */
+ if (x < 512) return 500; /* 0.5 seconds */
+ if (x < 768) return 250; /* 0.25 seconds */
+ if (x < 896) return 100; /* 0.1 seconds */
+ if (x < 960) return 50; /* 0.05 seconds */
+ if (x < 1024) return 25; /* 0.025 seconds */
+ return 0; /* infinite */
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmReadUSBInput --
+ * Read the new events from the device, and enqueue them.
+ *
+ ***************************************************************************
+ */
+static void
+xf86WcmReadUSBInput(LocalDevicePtr local)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) local->private;
+ WacomCommonPtr common = priv->common;
+ int serial = common->wcmLastSerial;
+ int is_proximity = priv->oldProximity;
+ int x = priv->oldX;
+ int y = priv->oldY;
+ int pressure = priv->oldZ;
+ int buttons = priv->oldButtons;
+ int tilt_x = priv->oldTiltX;
+ int tilt_y = priv->oldTiltY;
+ int wheel = priv->oldWheel;
+ int sampleTime, ticks;
+
+ ssize_t len;
+ int idx, loop;
+ struct input_event * event, *readevent;
+ char eventbuf[sizeof(struct input_event) * MAX_EVENTS];
+#define MOD_BUTTONS(bit, value) \
+ { int _b=bit, _v=value; buttons = (((_v) != 0) ? (buttons | _b) : (buttons & ~ _b)); }
+
+ /* get the sample time */
+ sampleTime = GetTimeInMillis();
+
+ /* account for roll overs and initialization */
+ if ((priv->throttleStart > sampleTime) || (!priv->throttleStart))
+ {
+ priv->throttleStart = sampleTime;
+ priv->throttleLimit = -1;
+ }
+
+ SYSCALL(len = read(local->fd, eventbuf, sizeof(eventbuf)));
+
+ DBG(10, ErrorF("xf86WcmReadUSBInput read %d events\n", len/sizeof(struct input_event)));
+
+ if (len <= 0) {
+ ErrorF("Error reading wacom device : %s\n", strerror(errno));
+ return;
+ }
+
+ for (readevent=(struct input_event *)eventbuf;
+ readevent<(struct input_event *)(eventbuf+len); readevent++) {
+ /* sanity check */
+ if (common->wcmIndex >= MAX_USB_EVENTS) {
+ DBG(11, ErrorF("xf86WcmReadUSBInput resetting buffer index\n"));
+ common->wcmIndex = 0;
+ }
+
+ common->wcmEvent[common->wcmIndex++] = *readevent;
+
+ /* MSC_SERIAL is the event terminator */
+ if (!((readevent->type == EV_MSC && readevent->code == MSC_SERIAL) ||
+ (readevent->type == EV_ABS && readevent->code == ABS_MISC))) {
+ continue;
+ }
+
+ for(loop=0; loop<common->wcmIndex; loop++) {
+ event = common->wcmEvent + loop;
+ DBG(11, ErrorF("xf86WcmReadUSBInput event[%d]->type=%d code=%d value=%d\n",
+ loop, event->type, event->code, event->value));
+ switch (event->type) {
+ case EV_ABS:
+ switch (event->code) {
+ case ABS_X:
+ x = event->value;
+ break;
+
+ case ABS_Y:
+ y = event->value;
+ break;
+
+ case ABS_TILT_X:
+ case ABS_RZ:
+ tilt_x = event->value;
+ break;
+
+ case ABS_TILT_Y:
+ tilt_y = event->value;
+ break;
+
+ case ABS_PRESSURE:
+ pressure = event->value;
+ MOD_BUTTONS (1, event->value > common->wcmThreshold ? 1 : 0);
+ break;
+
+ case ABS_DISTANCE:
+ /* This is not sent by the driver */
+ /* JEJ - actually it is, but it's not very useful */
+ break;
+
+ case ABS_MISC:
+ serial = event->value;
+ DBG(10, ErrorF("wacom tool serial id=%d\n", serial));
+ break;
+
+ case ABS_WHEEL:
+ wheel = event->value;
+ break;
+
+ case ABS_THROTTLE:
+ priv->throttleValue = event->value;
+ ticks = ThrottleToRate(event->value);
+ priv->throttleLimit = ticks ? priv->throttleStart + ticks : -1;
+ break;
+ }
+ break; /* EV_ABS */
+
+ case EV_REL:
+ switch (event->code) {
+ case REL_WHEEL:
+ wheel += event->value;
+ break;
+ default:
+ ErrorF("wacom: relative event received (%d)!!!\n", event->code);
+ break;
+ }
+ break; /* EV_REL */
+
+ case EV_KEY:
+ switch (event->code) {
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_AIRBRUSH:
+ DBG(10, ErrorF("USB Stylus detected %x\n", event->code));
+ common->wcmLastTool = STYLUS_ID;
+ is_proximity = (event->value != 0);
+ break;
+
+ case BTN_TOOL_RUBBER:
+ DBG(10, ErrorF("USB eraser detected %x\n", event->code));
+ common->wcmLastTool = ERASER_ID;
+ is_proximity = (event->value != 0);
+ break;
+
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ DBG(10, ErrorF("USB mouse detected %x\n", event->code));
+ common->wcmLastTool = CURSOR_ID;
+ is_proximity = (event->value != 0);
+ break;
+
+ case BTN_TOUCH:
+ /* we use the pressure to determine the button 1 */
+ break;
+
+ case BTN_STYLUS:
+ case BTN_MIDDLE:
+ MOD_BUTTONS (2, event->value);
+ break;
+
+ case BTN_STYLUS2:
+ case BTN_RIGHT:
+ MOD_BUTTONS (4, event->value);
+ break;
+
+ case BTN_LEFT:
+ MOD_BUTTONS (1, event->value);
+ break;
+
+ case BTN_SIDE:
+ MOD_BUTTONS (8, event->value);
+ break;
+
+ case BTN_EXTRA:
+ MOD_BUTTONS (16, event->value);
+ break;
+ }
+ break; /* EV_KEY */
+ case EV_MSC:
+ switch (event->code) {
+ case MSC_SERIAL:
+ serial = event->value;
+ DBG(10, ErrorF("wacom tool serial id=%d\n", serial));
+ break;
+ }
+ break; /* EV_MSC */
+ } /* switch event->type */
+
+ /* MSC_SERIAL is the event terminator */
+ if (!(event->type == EV_MSC && event->code == MSC_SERIAL) &&
+ !(event->type == EV_ABS && event->code == ABS_MISC)) {
+ continue;
+ }
+
+ /* handle throttle */
+ if ((priv->throttleLimit >= 0) && (priv->throttleLimit < sampleTime))
+ {
+ DBG(6, ErrorF("LIMIT REACHED: s=%d l=%d n=%d v=%d N=%d\n",
+ priv->throttleStart,
+ priv->throttleLimit,
+ sampleTime,
+ priv->throttleValue,
+ sampleTime + ThrottleToRate(priv->throttleValue)));
+
+ wheel += (priv->throttleValue > 0) ? 1 :
+ (priv->throttleValue < 0) ? -1 : 0;
+
+ priv->throttleStart = sampleTime;
+ priv->throttleLimit = sampleTime + ThrottleToRate(priv->throttleValue);
+ }
+
+ if ((is_proximity == priv->oldProximity) &&
+ (buttons == priv->oldButtons) &&
+ (wheel == priv->oldWheel) &&
+ (ABS(x - priv->oldX) <= common->wcmSuppress) &&
+ (ABS(y - priv->oldY) <= common->wcmSuppress) &&
+ (ABS(pressure - priv->oldZ) < 3) &&
+ (ABS(tilt_x - priv->oldTiltX) < 3) &&
+ (ABS(tilt_y - priv->oldTiltY) < 3)) {
+ DBG(10, ErrorF("filtered %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ x, priv->oldX,
+ y, priv->oldY,
+ pressure, priv->oldZ,
+ is_proximity, priv->oldProximity,
+ buttons, priv->oldButtons,
+ wheel, priv->oldWheel,
+ tilt_x, priv->oldTiltX,
+ tilt_y, priv->oldTiltY));
+ continue;
+ }
+
+ DBG(10, ErrorF("sending event %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ x, priv->oldX,
+ y, priv->oldY,
+ pressure, priv->oldZ,
+ is_proximity, priv->oldProximity,
+ buttons, priv->oldButtons,
+ wheel, priv->oldWheel,
+ tilt_x, priv->oldTiltX,
+ tilt_y, priv->oldTiltY));
+
+ for (idx=0; idx<common->wcmNumDevices; idx++) {
+ WacomDevicePtr dev = common->wcmDevices[idx]->private;
+ int id;
+
+ id = DEVICE_ID (dev->flags);
+
+ /* Find the device the current events are meant for */
+ if (id == common->wcmLastTool) {
+ DBG(11, ErrorF("tool id=%d for %s\n", id, local->name));
+
+ xf86WcmSendEvents(common->wcmDevices[idx],
+ common->wcmLastTool,
+ serial,
+ (common->wcmLastTool == STYLUS_ID || common->wcmLastTool == ERASER_ID),
+ !!(buttons),
+ is_proximity,
+ x, y, pressure, buttons,
+ tilt_x, tilt_y, wheel);
+ }
+ }
+ priv->oldX = x;
+ priv->oldY = y;
+ priv->oldZ = pressure;
+ priv->oldTiltX = tilt_x;
+ priv->oldTiltY = tilt_y;
+ priv->oldProximity = is_proximity;
+ priv->oldButtons = buttons;
+ priv->oldWheel = wheel;
+ common->wcmLastSerial = serial;
+ } /* next event */
+ common->wcmIndex=0;
+ } /* next event group */
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmUSBOpen --
+ *
+ ***************************************************************************
+ */
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define LONG(x) ((x)/BITS_PER_LONG)
+
+static Bool
+xf86WcmUSBOpen(LocalDevicePtr local)
+{
+ int err = 0;
+ WacomDevicePtr priv = (WacomDevicePtr)local->private;
+ WacomCommonPtr common = priv->common;
+ char name[256] = "Unknown";
+ int abs[5];
+ unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
+ int i, j;
+
+#ifdef XFREE86_V4
+ local->fd = xf86OpenSerial(local->options);
+#else
+ SYSCALL(local->fd = open(common->wcmDevice, O_RDONLY|O_NDELAY, 0));
+#endif
+ if (local->fd == -1) {
+ ErrorF("Error opening %s : %s\n", common->wcmDevice, strerror(errno));
+ return !Success;
+ }
+
+ ioctl(local->fd, EVIOCGNAME(sizeof(name)), name);
+ ErrorF("%s Wacom Kernel Input device name: \"%s\"\n", XCONFIG_PROBED, name);
+
+ memset(bit, 0, sizeof(bit));
+ ioctl(local->fd, EVIOCGBIT(0, EV_MAX), bit[0]);
+
+ for (i = 0; i < EV_MAX; i++)
+ if (test_bit(i, bit[0])) {
+ ioctl(local->fd, EVIOCGBIT(i, KEY_MAX), bit[i]);
+ for (j = 0; j < KEY_MAX; j++)
+ if (test_bit(j, bit[i])) {
+ if (i == EV_ABS) {
+ ioctl(local->fd, EVIOCGABS(j), abs);
+ switch (j) {
+ case ABS_X:
+ if (common->wcmMaxX == 0) {
+ common->wcmMaxX = abs[2];
+ }
+ break;
+
+ case ABS_Y:
+ if (common->wcmMaxY == 0) {
+ common->wcmMaxY = abs[2];
+ }
+ break;
+
+ case ABS_PRESSURE:
+ if (common->wcmMaxZ == DEFAULT_MAXZ) {
+ common->wcmMaxZ = abs[2];
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ DBG(2, ErrorF("setup is max X=%d(%d) Y=%d(%d) Z=%d(%d)\n",
+ common->wcmMaxX, common->wcmResolX,
+ common->wcmMaxY, common->wcmResolY,
+ common->wcmMaxZ, common->wcmResolZ));
+
+ /* send the tilt mode command after setup because it must be enabled */
+ /* after multi-mode to take precedence */
+ if (HANDLE_TILT(common)) {
+ /* Unfortunately, the USB driver doesn't allow to send this
+ * command to the tablet. Any other solutions ? */
+ DBG(2, ErrorF("Sending tilt mode order\n"));
+ }
+
+ if (common->wcmSuppress < 0) {
+ int xratio = common->wcmMaxX/screenInfo.screens[0]->width;
+ int yratio = common->wcmMaxY/screenInfo.screens[0]->height;
+
+ common->wcmSuppress = (xratio > yratio) ? yratio : xratio;
+ }
+
+ if (common->wcmSuppress > 100) {
+ common->wcmSuppress = 99;
+ }
+ /* Cannot send WC_SUPPRESS to the table. Will have to do
+ * this manually. */
+
+ priv->topX = 0;
+ priv->bottomX = common->wcmMaxX;
+ priv->topY = 0;
+ priv->bottomY = common->wcmMaxY;
+
+ if (xf86Verbose)
+ ErrorF("%s Wacom tablet maximum X=%d maximum Y=%d "
+ "X resolution=%d Y resolution=%d suppress=%d%s\n",
+ XCONFIG_PROBED, common->wcmMaxX, common->wcmMaxY,
+ common->wcmResolX, common->wcmResolY, common->wcmSuppress,
+ HANDLE_TILT(common) ? " Tilt" : "");
+
+ if (err < 0) {
+ ErrorF("ERROR: %d\n", err);
+ SYSCALL(close(local->fd));
+ return !Success;
+ }
+
+ /* to have the button field handled as a bit field */
+ common->wcmProtocolLevel = 5;
+
+ return Success;
+}
+
+#endif /* LINUX_INPUT */
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmControlProc --
+ *
+ ***************************************************************************
+ */
+static void
+xf86WcmControlProc(DeviceIntPtr device,
+ PtrCtrl *ctrl)
+{
+ DBG(2, ErrorF("xf86WcmControlProc\n"));
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmOpen --
+ *
+ ***************************************************************************
+ */
+#ifdef XFREE86_V4
+#define WAIT(t) \
+ err = xf86WaitForInput(-1, ((t) * 1000)); \
+ if (err == -1) { \
+ ErrorF("Wacom select error : %s\n", strerror(errno)); \
+ return !Success; \
+ }
+#else
+#define WAIT(t) \
+ timeout.tv_sec = 0; \
+ timeout.tv_usec = (t) * 1000; \
+ SYSCALL(err = select(0, NULL, NULL, NULL, &timeout)); \
+ if (err == -1) { \
+ ErrorF("Wacom select error : %s\n", strerror(errno)); \
+ return !Success; \
+ }
+#endif
+
+static Bool
+xf86WcmOpen(LocalDevicePtr local)
+{
+#ifndef XFREE86_V4
+ struct timeval timeout;
+#endif
+ char buffer[256];
+ char header[64]; /* This is a small buffer for discarding the unwanted header */
+ int err;
+ WacomDevicePtr priv = (WacomDevicePtr)local->private;
+ WacomCommonPtr common = priv->common;
+ int a, b;
+ int loop, idx;
+ float version = 0.0;
+ int is_a_penpartner = 0;
+
+ DBG(1, ErrorF("opening %s\n", common->wcmDevice));
+
+#ifdef XFREE86_V4
+ local->fd = xf86OpenSerial(local->options);
+#else
+ SYSCALL(local->fd = open(common->wcmDevice, O_RDWR|O_NDELAY, 0));
+#endif
+ if (local->fd < 0) {
+ ErrorF("Error opening %s : %s\n", common->wcmDevice, strerror(errno));
+ return !Success;
+ }
+
+#ifdef LINUX_INPUT
+ DBG(1, ErrorF("testing USB\n"));
+
+ if (xf86WcmIsUSBLine(local->fd)) {
+ int loop;
+
+ SYSCALL(close(local->fd));
+
+ for(loop=0; loop<common->wcmNumDevices; loop++) {
+ common->wcmDevices[loop]->read_input=xf86WcmReadUSBInput;
+ }
+ common->wcmOpen=xf86WcmUSBOpen;
+
+ return xf86WcmUSBOpen(local);
+ }
+#endif
+
+ DBG(1, ErrorF("initializing tablet\n"));
+
+ /* Set the speed of the serial link to 38400 */
+#ifdef XFREE86_V4
+ if (xf86SetSerialSpeed(local->fd, 38400) < 0) {
+ return !Success;
+ }
+#else
+ if (set_serial_speed(local->fd, B38400) == !Success)
+ return !Success;
+#endif
+
+ /* Send reset to the tablet */
+ SYSCALL(err = write(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 250 mSecs */
+ WAIT(250);
+
+ /* Send reset to the tablet */
+ SYSCALL(err = write(local->fd, WC_RESET, strlen(WC_RESET)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 75 mSecs */
+ WAIT(75);
+
+ /* Set the speed of the serial link to 19200 */
+#ifdef XFREE86_V4
+ if (xf86SetSerialSpeed(local->fd, 19200) < 0) {
+ return !Success;
+ }
+#else
+ if (set_serial_speed(local->fd, B19200) == !Success)
+ return !Success;
+#endif
+
+ /* Send reset to the tablet */
+ SYSCALL(err = write(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 250 mSecs */
+ WAIT(250);
+
+ /* Send reset to the tablet */
+ SYSCALL(err = write(local->fd, WC_RESET, strlen(WC_RESET)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 75 mSecs */
+ WAIT(75);
+
+ /* Set the speed of the serial link to 9600 */
+#ifdef XFREE86_V4
+ if (xf86SetSerialSpeed(local->fd, 9600) < 0) {
+ return !Success;
+ }
+#else
+ if (set_serial_speed(local->fd, B9600) == !Success)
+ return !Success;
+#endif
+
+ /* Send reset to the tablet */
+ SYSCALL(err = write(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 250 mSecs */
+ WAIT(250);
+
+ SYSCALL(err = write(local->fd, WC_STOP, strlen(WC_STOP)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 30 mSecs */
+ WAIT(30);
+
+#ifdef XFREE86_V4
+ xf86FlushInput(local->fd);
+#else
+ flush_input_fd(local->fd);
+#endif
+
+ DBG(2, ErrorF("reading model\n"));
+ if (!send_request(local->fd, WC_MODEL, buffer)) {
+ return !Success;
+ }
+ DBG(2, ErrorF("%s\n", buffer));
+
+ if (xf86Verbose) {
+ ErrorF("%s Wacom tablet model : %s\n", XCONFIG_PROBED, buffer+2);
+ }
+
+ /* Answer is in the form ~#Tablet-Model VRom_Version */
+ /* look for the first V from the end of the string */
+ /* this seems to be the better way to find the version of the ROM */
+ for(loop=strlen(buffer); loop>=0 && *(buffer+loop) != 'V'; loop--);
+ for(idx=loop; idx<strlen(buffer) && *(buffer+idx) != '-'; idx++);
+ *(buffer+idx) = '\0';
+
+ /* Extract version numbers */
+ sscanf(buffer+loop+1, "%f", &version);
+
+ if ((buffer[2] == 'G' && buffer[3] == 'D') ||
+ (buffer[2] == 'X' && buffer[3] == 'D')) {
+ DBG(2, ErrorF("detected an Intuos model\n"));
+ common->wcmProtocolLevel = 5;
+ common->wcmMaxZ = 1023; /* max Z value */
+ common->wcmResolX = 2540; /* X resolution in points/inch */
+ common->wcmResolY = 2540; /* Y resolution in points/inch */
+ common->wcmResolZ = 2540; /* Z resolution in points/inch */
+ common->wcmPktLength = 9; /* length of a packet */
+ if (common->wcmThreshold == INVALID_THRESHOLD) {
+ common->wcmThreshold = -480; /* Threshold for counting pressure as a button */
+ if (xf86Verbose) {
+ ErrorF("%s Wacom using pressure threshold of %d for button 1\n",
+ XCONFIG_PROBED, common->wcmThreshold);
+ }
+ }
+ }
+
+ /* Tilt works on ROM 1.4 and above */
+ DBG(2, ErrorF("wacom flags=%d ROM version=%f buffer=%s\n",
+ common->wcmFlags, version, buffer+loop+1));
+ if (common->wcmProtocolLevel == 4 &&
+ (common->wcmFlags & TILT_FLAG) && (version >= (float)1.4)) {
+ common->wcmPktLength = 9;
+ }
+
+ /* Check for a PenPartner or Graphire model which doesn't answer WC_CONFIG
+ * request. The Graphire model is handled like a PenPartner except that
+ * it doesn't answer WC_COORD requests.
+ */
+ if ((buffer[2] == 'C' || buffer[2] == 'E') && buffer[3] == 'T') {
+ if (buffer[2] == 'E') {
+ DBG(2, ErrorF("detected a Graphire model\n"));
+ common->wcmFlags |= GRAPHIRE_FLAG;
+ /* Graphire models don't answer WC_COORD requests */
+ common->wcmMaxX = 5103;
+ common->wcmMaxY = 3711;
+ common->wcmMaxZ = 512;
+ }
+ else {
+ DBG(2, ErrorF("detected a PenPartner model\n"));
+ common->wcmMaxZ = 256;
+ }
+ common->wcmResolX = 1000;
+ common->wcmResolY = 1000;
+ is_a_penpartner = 1;
+ }
+ else if (common->wcmProtocolLevel == 4 && !(common->wcmResolX && common->wcmResolY)) {
+ DBG(2, ErrorF("reading config\n"));
+ if (send_request(local->fd, WC_CONFIG, buffer)) {
+ DBG(2, ErrorF("%s\n", buffer));
+ /* The header string is simply a place to put the unwanted
+ * config header don't use buffer+xx because the header size
+ * varies on different tablets
+ */
+ if (sscanf(buffer, "%[^,],%d,%d,%d,%d", header, &a, &b, &common->wcmResolX, &common->wcmResolY) == 5) {
+ DBG(6, ErrorF("WC_CONFIG Header = %s\n", header));
+ }
+ else {
+ ErrorF("WACOM: unable to parse resolution. Using default.\n");
+ common->wcmResolX = common->wcmResolY = 1270;
+ }
+ }
+ else {
+ ErrorF("WACOM: unable to read resolution. Using default.\n");
+ common->wcmResolX = common->wcmResolY = 1270;
+ }
+ }
+
+ if (!(common->wcmFlags & GRAPHIRE_FLAG) && !(common->wcmMaxX && common->wcmMaxY)) {
+ DBG(2, ErrorF("reading max coordinates\n"));
+ if (!send_request(local->fd, WC_COORD, buffer)) {
+ ErrorF("WACOM: unable to read max coordinates. Use the MaxX and MaxY options.\n");
+ return !Success;
+ }
+ DBG(2, ErrorF("%s\n", buffer));
+ if (sscanf(buffer+2, "%d,%d", &common->wcmMaxX, &common->wcmMaxY) != 2) {
+ ErrorF("WACOM: unable to parse max coordinates. Use the MaxX and MaxY options.\n");
+ return !Success;
+ }
+ }
+
+ DBG(2, ErrorF("setup is max X=%d max Y=%d resol X=%d resol Y=%d\n",
+ common->wcmMaxX, common->wcmMaxY, common->wcmResolX,
+ common->wcmResolY));
+
+ /* We can't change the resolution on PenPartner and Graphire models */
+ if (!is_a_penpartner && common->wcmProtocolLevel == 4) {
+ int resolX = common->wcmResolX, resolY = common->wcmResolY;
+
+ /* Force the resolution.
+ */
+ if (((float)version) >= 1.2) {
+ resolX = resolY = 2540;
+ }
+ sprintf(buffer, "%s%d\r", WC_NEW_RESOLUTION, resolX);
+ SYSCALL(err = write(local->fd, buffer, strlen(buffer)));
+
+ /* Verify the resolution change.
+ */
+ DBG(2, ErrorF("rereading config\n"));
+ if (send_request(local->fd, WC_CONFIG, buffer)) {
+ DBG(2, ErrorF("%s\n", buffer));
+ /* The header string is simply a place to put the unwanted
+ * config header don't use buffer+xx because the header size
+ * varies on different tablets
+ */
+ if (sscanf(buffer, "%[^,],%d,%d,%d,%d", header, &a, &b, &common->wcmResolX, &common->wcmResolY) == 5) {
+ DBG(6, ErrorF("WC_CONFIG Header = %s\n", header));
+ }
+ else {
+ ErrorF("WACOM: unable to reparse resolution. Using previous values.\n");
+ }
+ }
+ else {
+ ErrorF("WACOM: unable to reread resolution. Using previous values.\n");
+ }
+
+ /* The following couple of lines convert the MaxX and MaxY returned by
+ * the Wacom from 1270lpi to the Wacom's active resolution.
+ */
+ common->wcmMaxX = (common->wcmMaxX / MAX_COORD_RES) * common->wcmResolX;
+ common->wcmMaxY = (common->wcmMaxY / MAX_COORD_RES) * common->wcmResolY;
+ }
+
+ DBG(2, ErrorF("setup is max X=%d max Y=%d resol X=%d resol Y=%d\n",
+ common->wcmMaxX, common->wcmMaxY, common->wcmResolX,
+ common->wcmResolY));
+
+ /* Send a setup string to the tablet */
+ if (is_a_penpartner) {
+ SYSCALL(err = write(local->fd, penpartner_setup_string,
+ strlen(penpartner_setup_string)));
+ }
+ else if (common->wcmProtocolLevel == 4) {
+ SYSCALL(err = write(local->fd, WC_RESET, strlen(WC_RESET)));
+ WAIT(75);
+ SYSCALL(err = write(local->fd, setup_string, strlen(setup_string)));
+ }
+ else {
+ SYSCALL(err = write(local->fd, intuos_setup_string,
+ strlen(intuos_setup_string)));
+ }
+
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Send the tilt mode command after setup because it must be enabled */
+ /* after multi-mode to take precedence */
+ if (common->wcmProtocolLevel == 4 && HANDLE_TILT(common)) {
+ DBG(2, ErrorF("Sending tilt mode order\n"));
+
+ SYSCALL(err = write(local->fd, WC_TILT_MODE, strlen(WC_TILT_MODE)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+ }
+
+ if (common->wcmSuppress < 0) {
+ int xratio = common->wcmMaxX/screenInfo.screens[0]->width;
+ int yratio = common->wcmMaxY/screenInfo.screens[0]->height;
+
+ common->wcmSuppress = (xratio > yratio) ? yratio : xratio;
+ }
+
+ if (common->wcmSuppress > 100) {
+ common->wcmSuppress = 99;
+ }
+
+ if (common->wcmProtocolLevel == 4) {
+ char buf[20];
+
+ sprintf(buf, "%s%d\r", WC_SUPPRESS, common->wcmSuppress);
+ SYSCALL(err = write(local->fd, buf, strlen(buf)));
+
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+ }
+
+ if (xf86Verbose)
+ ErrorF("%s Wacom %s tablet maximum X=%d maximum Y=%d "
+ "X resolution=%d Y resolution=%d suppress=%d%s\n",
+ XCONFIG_PROBED, common->wcmProtocolLevel == 4 ? "IV" : "V",
+ common->wcmMaxX, common->wcmMaxY,
+ common->wcmResolX, common->wcmResolY, common->wcmSuppress,
+ HANDLE_TILT(common) ? " Tilt" : "");
+
+ if (err <= 0) {
+ SYSCALL(close(local->fd));
+ return !Success;
+ }
+
+ /* change the serial speed if requested */
+ if (common->wcmLinkSpeed > 9600) {
+ if (common->wcmProtocolLevel == 5) {
+ char *speed_init_string = WC_V_19200;
+#ifndef XFREE86_V4
+ int speed = B19200;
+#endif
+ DBG(1, ErrorF("Switching serial link to %d\n", common->wcmLinkSpeed));
+
+ if (common->wcmLinkSpeed == 38400 && version < 2.0) {
+ ErrorF("Wacom: 38400 speed not supported with this Intuos firmware (%f)\n", version);
+ ErrorF("Switching to 19200\n");
+ common->wcmLinkSpeed = 19200;
+ }
+
+ switch (common->wcmLinkSpeed) {
+ case 38400:
+ speed_init_string = WC_V_38400;
+#ifndef XFREE86_V4
+ speed = B38400;
+#endif
+ break;
+
+ case 19200:
+ speed_init_string = WC_V_19200;
+#ifndef XFREE86_V4
+ speed = B19200;
+#endif
+ break;
+ }
+ /* Switch the tablet to the requested speed */
+ SYSCALL(err = write(local->fd, speed_init_string, strlen(speed_init_string)));
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ /* Wait 75 mSecs */
+ WAIT(75);
+
+ /* Set the speed of the serial link to requested speed */
+#ifdef XFREE86_V4
+ if (xf86SetSerialSpeed(local->fd, common->wcmLinkSpeed) < 0) {
+ return !Success;
+ }
+#else
+ if (set_serial_speed(local->fd, speed) == !Success)
+ return !Success;
+#endif
+ }
+ else {
+ ErrorF("Changing the speed of a wacom IV device is not yet implemented\n");
+ }
+ }
+
+ /* Tell the tablet to start sending coordinates */
+ SYSCALL(err = write(local->fd, WC_START, strlen(WC_START)));
+
+ if (err == -1) {
+ ErrorF("Wacom write error : %s\n", strerror(errno));
+ return !Success;
+ }
+
+ return Success;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmOpenDevice --
+ * Open the physical device and init information structs.
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmOpenDevice(DeviceIntPtr pWcm)
+{
+ LocalDevicePtr local = (LocalDevicePtr)pWcm->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr)PRIVATE(pWcm);
+ WacomCommonPtr common = priv->common;
+ double screenRatio, tabletRatio;
+ int gap;
+ int loop;
+ int screen_idx = 0;
+
+ if (local->fd < 0) {
+ if (common->wcmInitNumber > 2 ||
+ priv->initNumber == common->wcmInitNumber) {
+ if (common->wcmOpen(local) != Success) {
+ if (local->fd >= 0) {
+ SYSCALL(close(local->fd));
+ }
+ local->fd = -1;
+ }
+ else {
+ /* report the file descriptor to all devices */
+ for(loop=0; loop<common->wcmNumDevices; loop++) {
+ common->wcmDevices[loop]->fd = local->fd;
+ }
+ }
+ common->wcmInitNumber++;
+ priv->initNumber = common->wcmInitNumber;
+ }
+ else {
+ priv->initNumber = common->wcmInitNumber;
+ }
+ }
+
+ if (local->fd != -1 &&
+ priv->factorX == 0.0) {
+
+ if (priv->bottomX == 0) priv->bottomX = common->wcmMaxX;
+
+ if (priv->bottomY == 0) priv->bottomY = common->wcmMaxY;
+
+ /* Verify Box validity */
+
+ if (priv->topX > common->wcmMaxX ||
+ priv->topX < 0) {
+ ErrorF("Wacom invalid TopX (%d) reseting to 0\n", priv->topX);
+ priv->topX = 0;
+ }
+
+ if (priv->topY > common->wcmMaxY ||
+ priv->topY < 0) {
+ ErrorF("Wacom invalid TopY (%d) reseting to 0\n", priv->topY);
+ priv->topY = 0;
+ }
+
+ if (priv->bottomX > common->wcmMaxX ||
+ priv->bottomX < priv->topX) {
+ ErrorF("Wacom invalid BottomX (%d) reseting to %d\n",
+ priv->bottomX, common->wcmMaxX);
+ priv->bottomX = common->wcmMaxX;
+ }
+
+ if (priv->bottomY > common->wcmMaxY ||
+ priv->bottomY < priv->topY) {
+ ErrorF("Wacom invalid BottomY (%d) reseting to %d\n",
+ priv->bottomY, common->wcmMaxY);
+ priv->bottomY = common->wcmMaxY;
+ }
+
+ if (priv->screen_no != -1 &&
+ (priv->screen_no >= screenInfo.numScreens ||
+ priv->screen_no < 0)) {
+ ErrorF("%s: invalid screen number %d, resetting to 0\n",
+ local->name, priv->screen_no);
+ priv->screen_no = 0;
+ }
+
+ /* Calculate the ratio according to KeepShape, TopX and TopY */
+
+ if (priv->screen_no != -1) {
+ screen_idx = priv->screen_no;
+ }
+
+ if (priv->flags & KEEP_SHAPE_FLAG) {
+ screenRatio = ((double) screenInfo.screens[screen_idx]->width)
+ / screenInfo.screens[screen_idx]->height;
+
+ tabletRatio = ((double) (common->wcmMaxX - priv->topX))
+ / (common->wcmMaxY - priv->topY);
+
+ DBG(2, ErrorF("screenRatio = %.3g, tabletRatio = %.3g\n",
+ screenRatio, tabletRatio));
+
+ if (screenRatio > tabletRatio) {
+ gap = common->wcmMaxY * (1 - tabletRatio/screenRatio);
+ priv->bottomX = common->wcmMaxX;
+ priv->bottomY = common->wcmMaxY - gap;
+ } else {
+ gap = common->wcmMaxX * (1 - screenRatio/tabletRatio);
+ priv->bottomX = common->wcmMaxX - gap;
+ priv->bottomY = common->wcmMaxY;
+ }
+ }
+ priv->factorX = ((double) screenInfo.screens[0]->width)
+ / (priv->bottomX - priv->topX);
+ priv->factorY = ((double) screenInfo.screens[0]->height)
+ / (priv->bottomY - priv->topY);
+
+ if (xf86Verbose)
+ ErrorF("%s Wacom tablet top X=%d top Y=%d "
+ "bottom X=%d bottom Y=%d\n",
+ XCONFIG_PROBED, priv->topX, priv->topY,
+ priv->bottomX, priv->bottomY);
+
+ DBG(2, ErrorF("X factor = %.3g, Y factor = %.3g\n",
+ priv->factorX, priv->factorY));
+ }
+
+ /* Check threshold correctness */
+ DBG(2, ErrorF("Threshold=%d\n", common->wcmThreshold));
+
+ if (common->wcmThreshold > common->wcmMaxZ ||
+ common->wcmThreshold < 0) {
+ if (((common->wcmProtocolLevel == 5) ||
+ (common->wcmFlags & GRAPHIRE_FLAG)) &&
+ xf86Verbose &&
+ common->wcmThreshold != INVALID_THRESHOLD)
+ ErrorF("%s Wacom invalid threshold %d. Reset to %d\n",
+ XCONFIG_PROBED, common->wcmThreshold, common->wcmMaxZ / 3);
+ common->wcmThreshold = common->wcmMaxZ / 3;
+ }
+ DBG(2, ErrorF("New threshold=%d\n", common->wcmThreshold));
+
+ /* Set the real values */
+ InitValuatorAxisStruct(pWcm,
+ 0,
+ 0, /* min val */
+ priv->bottomX - priv->topX, /* max val */
+ mils(common->wcmResolX), /* resolution */
+ 0, /* min_res */
+ mils(common->wcmResolX)); /* max_res */
+ InitValuatorAxisStruct(pWcm,
+ 1,
+ 0, /* min val */
+ priv->bottomY - priv->topY, /* max val */
+ mils(common->wcmResolY), /* resolution */
+ 0, /* min_res */
+ mils(common->wcmResolY)); /* max_res */
+ InitValuatorAxisStruct(pWcm,
+ 2,
+ 0, /* min val */
+ common->wcmMaxZ, /* max val */
+ mils(common->wcmResolZ), /* resolution */
+ 0, /* min_res */
+ mils(common->wcmResolZ)); /* max_res */
+ InitValuatorAxisStruct(pWcm,
+ 3,
+ -64, /* min val */
+ 63, /* max val */
+ 128, /* resolution ??? */
+ 0,
+ 128);
+ InitValuatorAxisStruct(pWcm,
+ 4,
+ -64, /* min val */
+ 63, /* max val */
+ 128, /* resolution ??? */
+ 0,
+ 128);
+ InitValuatorAxisStruct(pWcm,
+ 5,
+ 0, /* min val */
+ 1023, /* max val */
+ 128, /* resolution ??? */
+ 0,
+ 128);
+
+ return (local->fd != -1);
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmClose --
+ *
+ ***************************************************************************
+ */
+static void
+xf86WcmClose(LocalDevicePtr local)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)local->private;
+ WacomCommonPtr common = priv->common;
+ int loop;
+ int num = 0;
+
+ for(loop=0; loop<common->wcmNumDevices; loop++) {
+ if (common->wcmDevices[loop]->fd >= 0) {
+ num++;
+ }
+ }
+ DBG(4, ErrorF("Wacom number of open devices = %d\n", num));
+
+ if (num == 1) {
+ SYSCALL(close(local->fd));
+ }
+
+ local->fd = -1;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmProc --
+ * Handle the initialization, etc. of a wacom
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmProc(DeviceIntPtr pWcm,
+ int what)
+{
+ CARD8 map[(32 << 4) + 1];
+ int nbaxes;
+ int nbbuttons;
+ int loop;
+ LocalDevicePtr local = (LocalDevicePtr)pWcm->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr)PRIVATE(pWcm);
+
+ DBG(2, ErrorF("BEGIN xf86WcmProc dev=0x%x priv=0x%x type=%s flags=%d what=%d\n",
+ pWcm, priv, (DEVICE_ID(priv->flags) == STYLUS_ID) ? "stylus" :
+ (DEVICE_ID(priv->flags) == CURSOR_ID) ? "cursor" : "eraser",
+ priv->flags, what));
+
+ switch (what)
+ {
+ case DEVICE_INIT:
+ DBG(1, ErrorF("xf86WcmProc pWcm=0x%x what=INIT\n", pWcm));
+
+ nbaxes = 6; /* X, Y, Pressure, Tilt-X, Tilt-Y, Wheel */
+
+ switch(DEVICE_ID(priv->flags)) {
+ case ERASER_ID:
+ nbbuttons = 1;
+ break;
+ case STYLUS_ID:
+ nbbuttons = 4;
+ break;
+ default:
+ nbbuttons = 16;
+ break;
+ }
+
+ for(loop=1; loop<=nbbuttons; loop++) map[loop] = loop;
+
+ if (InitButtonClassDeviceStruct(pWcm,
+ nbbuttons,
+ map) == FALSE) {
+ ErrorF("unable to allocate Button class device\n");
+ return !Success;
+ }
+
+ if (InitFocusClassDeviceStruct(pWcm) == FALSE) {
+ ErrorF("unable to init Focus class device\n");
+ return !Success;
+ }
+
+ if (InitPtrFeedbackClassDeviceStruct(pWcm,
+ xf86WcmControlProc) == FALSE) {
+ ErrorF("unable to init ptr feedback\n");
+ return !Success;
+ }
+
+ if (InitProximityClassDeviceStruct(pWcm) == FALSE) {
+ ErrorF("unable to init proximity class device\n");
+ return !Success;
+ }
+
+ if (InitKeyClassDeviceStruct(pWcm, &wacom_keysyms, NULL) == FALSE) {
+ ErrorF("unable to init key class device\n");
+ return !Success;
+ }
+
+ if (InitValuatorClassDeviceStruct(pWcm,
+ nbaxes,
+ xf86GetMotionEvents,
+ local->history_size,
+ ((priv->flags & ABSOLUTE_FLAG)
+ ? Absolute : Relative) |
+ OutOfProximity)
+ == FALSE) {
+ ErrorF("unable to allocate Valuator class device\n");
+ return !Success;
+ }
+ else {
+ /* allocate the motion history buffer if needed */
+ xf86MotionHistoryAllocate(local);
+#ifndef XFREE86_V4
+ AssignTypeAndName(pWcm, local->atom, local->name);
+#endif
+ }
+
+ /* open the device to gather informations */
+ xf86WcmOpenDevice(pWcm);
+
+ break;
+
+ case DEVICE_ON:
+ DBG(1, ErrorF("xf86WcmProc pWcm=0x%x what=ON\n", pWcm));
+
+ if ((local->fd < 0) && (!xf86WcmOpenDevice(pWcm))) {
+ return !Success;
+ }
+#ifdef XFREE86_V4
+ xf86AddEnabledDevice(local);
+#else
+ AddEnabledDevice(local->fd);
+#endif
+ pWcm->public.on = TRUE;
+ break;
+
+ case DEVICE_OFF:
+ case DEVICE_CLOSE:
+ DBG(1, ErrorF("xf86WcmProc pWcm=0x%x what=%s\n", pWcm,
+ (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
+ if (local->fd >= 0) {
+#ifdef XFREE86_V4
+ xf86RemoveEnabledDevice(local);
+#else
+ RemoveEnabledDevice(local->fd);
+#endif
+ xf86WcmClose(local);
+ }
+ pWcm->public.on = FALSE;
+ break;
+
+ default:
+ ErrorF("wacom unsupported mode=%d\n", what);
+ return !Success;
+ break;
+ }
+ DBG(2, ErrorF("END xf86WcmProc Success what=%d dev=0x%x priv=0x%x\n",
+ what, pWcm, priv));
+ return Success;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmChangeControl --
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmChangeControl(LocalDevicePtr local,
+ xDeviceCtl *control)
+{
+ xDeviceResolutionCtl *res;
+ int *resolutions;
+ char str[10];
+
+ res = (xDeviceResolutionCtl *)control;
+
+ if ((control->control != DEVICE_RESOLUTION) ||
+ (res->num_valuators < 1))
+ return (BadMatch);
+
+ resolutions = (int *)(res +1);
+
+ DBG(3, ErrorF("xf86WcmChangeControl changing to %d (suppressing under)\n",
+ resolutions[0]));
+
+ sprintf(str, "SU%d\r", resolutions[0]);
+ SYSCALL(write(local->fd, str, strlen(str)));
+
+ return(Success);
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmSwitchMode --
+ *
+ ***************************************************************************
+ */
+static int
+xf86WcmSwitchMode(ClientPtr client,
+ DeviceIntPtr dev,
+ int mode)
+{
+ LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr)local->private;
+
+ DBG(3, ErrorF("xf86WcmSwitchMode dev=0x%x mode=%d\n", dev, mode));
+
+ if (mode == Absolute) {
+ priv->flags = priv->flags | ABSOLUTE_FLAG;
+ }
+ else {
+ if (mode == Relative) {
+ priv->flags = priv->flags & ~ABSOLUTE_FLAG;
+ }
+ else {
+ DBG(1, ErrorF("xf86WcmSwitchMode dev=0x%x invalid mode=%d\n", dev,
+ mode));
+ return BadMatch;
+ }
+ }
+ return Success;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmAllocate --
+ *
+ ***************************************************************************
+ */
+static LocalDevicePtr
+xf86WcmAllocate(char * name,
+ int flag)
+{
+ LocalDevicePtr local;
+ WacomDevicePtr priv;
+ WacomCommonPtr common;
+
+ priv = (WacomDevicePtr) xalloc(sizeof(WacomDeviceRec));
+ if (!priv)
+ return NULL;
+
+ common = (WacomCommonPtr) xalloc(sizeof(WacomCommonRec));
+ if (!common) {
+ xfree(priv);
+ return NULL;
+ }
+
+#ifdef XFREE86_V4
+ local = xf86AllocateInput(wcmDrv, 0);
+#else
+ local = (LocalDevicePtr) xalloc(sizeof(LocalDeviceRec));
+#endif
+ if (!local) {
+ xfree(priv);
+ xfree(common);
+ return NULL;
+ }
+
+ local->name = name;
+ local->flags = 0;
+#ifndef XFREE86_V4
+ local->device_config = xf86WcmConfig;
+#endif
+ local->device_control = xf86WcmProc;
+ local->read_input = xf86WcmReadInput;
+ local->control_proc = xf86WcmChangeControl;
+ local->close_proc = xf86WcmClose;
+ local->switch_mode = xf86WcmSwitchMode;
+ local->conversion_proc = xf86WcmConvert;
+ local->reverse_conversion_proc = xf86WcmReverseConvert;
+ local->fd = -1;
+ local->atom = 0;
+ local->dev = NULL;
+ local->private = priv;
+ local->private_flags = 0;
+ local->history_size = 0;
+ local->old_x = -1;
+ local->old_y = -1;
+
+ priv->flags = flag; /* various flags (device type, absolute, first touch...) */
+ priv->oldX = -1; /* previous X position */
+ priv->oldY = -1; /* previous Y position */
+ priv->oldZ = -1; /* previous pressure */
+ priv->oldTiltX = -1; /* previous tilt in x direction */
+ priv->oldTiltY = -1; /* previous tilt in y direction */
+ priv->oldButtons = 0; /* previous buttons state */
+ priv->oldProximity = 1; /* previous proximity */
+ priv->oldWheel = 0; /* previous wheel */
+ priv->topX = 0; /* X top */
+ priv->topY = 0; /* Y top */
+ priv->bottomX = 0; /* X bottom */
+ priv->bottomY = 0; /* Y bottom */
+ priv->factorX = 0.0; /* X factor */
+ priv->factorY = 0.0; /* Y factor */
+ priv->common = common; /* common info pointer */
+ priv->oldProximity = 0; /* previous proximity */
+ priv->serial = 0; /* serial number */
+ priv->initNumber = 0; /* magic number for the init phasis */
+ priv->screen_no = -1; /* associated screen */
+
+ /* JEJ - throttle sampling code */
+ priv->throttleValue = 0;
+ priv->throttleStart = 0;
+ priv->throttleLimit = -1;
+
+ common->wcmDevice = ""; /* device file name */
+ common->wcmSuppress = -1; /* transmit position if increment is superior */
+ common->wcmFlags = 0; /* various flags */
+ common->wcmDevices = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr));
+ common->wcmDevices[0] = local;
+ common->wcmNumDevices = 1; /* number of devices */
+ common->wcmIndex = 0; /* number of bytes read */
+ common->wcmPktLength = 7; /* length of a packet */
+ common->wcmMaxX = 0; /* max X value */
+ common->wcmMaxY = 0; /* max Y value */
+ common->wcmMaxZ = DEFAULT_MAXZ; /* max Z value */
+ common->wcmResolX = 0; /* X resolution in points/inch */
+ common->wcmResolY = 0; /* Y resolution in points/inch */
+ common->wcmResolZ = 1270; /* Z resolution in points/inch */
+ common->wcmHasEraser = (flag & ERASER_ID) ? TRUE : FALSE; /* True if an eraser has been configured */
+ common->wcmStylusSide = TRUE; /* eraser or stylus ? */
+ common->wcmStylusProximity = FALSE; /* a stylus is in proximity ? */
+ common->wcmProtocolLevel = 4; /* protocol level */
+ common->wcmThreshold = INVALID_THRESHOLD; /* button 1 threshold for some tablet models */
+ common->wcmInitNumber = 0; /* magic number for the init phasis */
+ common->wcmLinkSpeed = 9600; /* serial link speed */
+ common->wcmOpen = xf86WcmOpen; /* function used to open the line (serial or USB) */
+ common->wcmLastSerial = 0; /* last device (used by the USB part) */
+ common->wcmLastTool = -1; /* last tool (used by the USB part) */
+ return local;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmAllocateStylus --
+ *
+ ***************************************************************************
+ */
+static LocalDevicePtr
+xf86WcmAllocateStylus()
+{
+ LocalDevicePtr local = xf86WcmAllocate(XI_STYLUS, STYLUS_ID);
+
+ if (local)
+ local->type_name = "Wacom Stylus";
+ return local;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmAllocateCursor --
+ *
+ ***************************************************************************
+ */
+static LocalDevicePtr
+xf86WcmAllocateCursor()
+{
+ LocalDevicePtr local = xf86WcmAllocate(XI_CURSOR, CURSOR_ID);
+
+ if (local)
+ local->type_name = "Wacom Cursor";
+ return local;
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86WcmAllocateEraser --
+ *
+ ***************************************************************************
+ */
+static LocalDevicePtr
+xf86WcmAllocateEraser()
+{
+ LocalDevicePtr local = xf86WcmAllocate(XI_ERASER, ABSOLUTE_FLAG|ERASER_ID);
+
+ if (local)
+ local->type_name = "Wacom Eraser";
+ return local;
+}
+
+/*
+ ***************************************************************************
+ *
+ * Wacom Stylus device association --
+ *
+ ***************************************************************************
+ */
+DeviceAssocRec wacom_stylus_assoc =
+{
+ STYLUS_SECTION_NAME, /* config_section_name */
+ xf86WcmAllocateStylus /* device_allocate */
+};
+
+/*
+ ***************************************************************************
+ *
+ * Wacom Cursor device association --
+ *
+ ***************************************************************************
+ */
+DeviceAssocRec wacom_cursor_assoc =
+{
+ CURSOR_SECTION_NAME, /* config_section_name */
+ xf86WcmAllocateCursor /* device_allocate */
+};
+
+/*
+ ***************************************************************************
+ *
+ * Wacom Eraser device association --
+ *
+ ***************************************************************************
+ */
+DeviceAssocRec wacom_eraser_assoc =
+{
+ ERASER_SECTION_NAME, /* config_section_name */
+ xf86WcmAllocateEraser /* device_allocate */
+};
+
+#ifndef XFREE86_V4
+#ifdef DYNAMIC_MODULE
+/*
+ ***************************************************************************
+ *
+ * entry point of dynamic loading
+ *
+ ***************************************************************************
+ */
+int
+#ifndef DLSYM_BUG
+init_module(unsigned long server_version)
+#else
+init_xf86Wacom(unsigned long server_version)
+#endif
+{
+ xf86AddDeviceAssoc(&wacom_stylus_assoc);
+ xf86AddDeviceAssoc(&wacom_cursor_assoc);
+ xf86AddDeviceAssoc(&wacom_eraser_assoc);
+
+ if (server_version != XF86_VERSION_CURRENT) {
+ ErrorF("Warning: Wacom module compiled for version%s\n", XF86_VERSION);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+#endif /* DYNAMIC_MODULE */
+
+#else /* XFREE86_V4 */
+
+#include "wcm-beta.h"
+
+/*
+ * xf86WcmUninit --
+ *
+ * called when the device is no longer needed.
+ */
+static void
+xf86WcmUninit(InputDriverPtr drv,
+ LocalDevicePtr local,
+ int flags)
+{
+ WacomDevicePtr priv;
+
+ priv = (WacomDevicePtr) local->private;
+
+ DBG(1, ErrorF("xf86WcmUninit\n"));
+
+ if (priv->flags & BETA_FLAG)
+ {
+ wcmBetaDeleteDevice(drv,local,flags);
+ return;
+ }
+
+ xf86WcmProc(local->dev, DEVICE_OFF);
+
+ xfree (priv);
+ xf86DeleteInput(local, 0);
+}
+
+/*
+ * xf86WcmInit --
+ *
+ * called when the module subsection is found in XF86Config
+ */
+static InputInfoPtr
+xf86WcmInit(InputDriverPtr drv,
+ IDevPtr dev,
+ int flags)
+{
+ LocalDevicePtr local = NULL;
+ LocalDevicePtr fakeLocal = NULL;
+ WacomDevicePtr priv = NULL;
+ WacomCommonPtr common = NULL;
+ char *s;
+ LocalDevicePtr localDevices;
+
+ wcmDrv = drv;
+
+ fakeLocal = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec));
+ if (!fakeLocal)
+ return NULL;
+
+ fakeLocal->conf_idev = dev;
+
+ /* Force default serial port options to exist because the serial init
+ * phasis is based on those values.
+ */
+ xf86CollectInputOptions(fakeLocal, default_options, NULL);
+
+ /* Type is mandatory */
+ s = xf86FindOptionValue(fakeLocal->options, "Type");
+
+ /* Beta code jumps out here */
+ if (s && (xf86NameCmp(s, "beta") == 0))
+ {
+ xfree(fakeLocal);
+ return wcmBetaNewDevice(drv,dev,flags);
+ }
+ else if (s && (xf86NameCmp(s, "stylus") == 0)) {
+ local = xf86WcmAllocateStylus();
+ }
+ else if (s && (xf86NameCmp(s, "cursor") == 0)) {
+ local = xf86WcmAllocateCursor();
+ }
+ else if (s && (xf86NameCmp(s, "eraser") == 0)) {
+ local = xf86WcmAllocateEraser();
+ }
+ else {
+ xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
+ "Must be one of stylus, cursor or eraser\n",
+ dev->identifier);
+ goto SetupProc_fail;
+ }
+
+ if (!local) {
+ xfree(fakeLocal);
+ return NULL;
+ }
+
+ priv = (WacomDevicePtr) local->private;
+ common = priv->common;
+
+ local->options = fakeLocal->options;
+ local->conf_idev = fakeLocal->conf_idev;
+ local->name = dev->identifier;
+ xfree(fakeLocal);
+
+ /* Serial Device is mandatory */
+ common->wcmDevice = xf86FindOptionValue(local->options, "Device");
+
+ if (!common->wcmDevice) {
+ xf86Msg (X_ERROR, "%s: No Device specified.\n", dev->identifier);
+ goto SetupProc_fail;
+ }
+
+ /* Lookup to see if there is another wacom device sharing
+ * the same serial line.
+ */
+ localDevices = xf86FirstLocalDevice();
+
+ while(localDevices) {
+ if ((local != localDevices) &&
+ (localDevices->device_control == xf86WcmProc) &&
+ (strcmp(((WacomDevicePtr)localDevices->private)->common->wcmDevice,
+ common->wcmDevice) == 0)) {
+ DBG(2, ErrorF("xf86WcmConfig wacom port share between"
+ " %s and %s\n",
+ local->name, localDevices->name));
+ ((WacomDevicePtr) localDevices->private)->common->wcmHasEraser |= common->wcmHasEraser;
+ xfree(common->wcmDevices);
+ xfree(common);
+ common = priv->common = ((WacomDevicePtr) localDevices->private)->common;
+ common->wcmNumDevices++;
+ common->wcmDevices = (LocalDevicePtr *) xrealloc(common->wcmDevices,
+ sizeof(LocalDevicePtr) * common->wcmNumDevices);
+ common->wcmDevices[common->wcmNumDevices - 1] = local;
+ break;
+ }
+ localDevices = localDevices->next;
+ }
+
+ /* Process the common options. */
+ xf86ProcessCommonOptions(local, local->options);
+
+ /* Optional configuration */
+
+ xf86Msg(X_CONFIG, "%s serial device is %s\n", dev->identifier,
+ common->wcmDevice);
+
+ debug_level = xf86SetIntOption(local->options, "DebugLevel", debug_level);
+ if (debug_level > 0) {
+ xf86Msg(X_CONFIG, "WACOM: debug level set to %d\n", debug_level);
+ }
+
+ s = xf86FindOptionValue(local->options, "Mode");
+
+ if (s && (xf86NameCmp(s, "absolute") == 0)) {
+ priv->flags = priv->flags | ABSOLUTE_FLAG;
+ }
+ else if (s && (xf86NameCmp(s, "relative") == 0)) {
+ priv->flags = priv->flags & ~ABSOLUTE_FLAG;
+ }
+ else if (s) {
+ xf86Msg(X_ERROR, "%s: invalid Mode (should be absolute or relative). Using default.\n",
+ dev->identifier);
+ }
+ xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name,
+ (priv->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");
+
+ common->wcmSuppress = xf86SetIntOption(local->options, "Suppress", common->wcmSuppress);
+ if (common->wcmSuppress != -1) {
+ xf86Msg(X_CONFIG, "WACOM: suppress value is %d\n", XCONFIG_GIVEN,
+ common->wcmSuppress);
+ }
+
+ if (xf86SetBoolOption(local->options, "Tilt", (common->wcmFlags & TILT_FLAG))) {
+ common->wcmFlags |= TILT_FLAG;
+ }
+
+#ifdef LINUX_INPUT
+ if (xf86SetBoolOption(local->options, "USB", (common->wcmOpen == xf86WcmUSBOpen))) {
+ local->read_input=xf86WcmReadUSBInput;
+ common->wcmOpen=xf86WcmUSBOpen;
+ xf86Msg(X_CONFIG, "%s: reading USB link\n", dev->identifier);
+ }
+#else
+ if (xf86SetBoolOption(local->options, "USB", 0)) {
+ ErrorF("The USB version of the driver isn't available for your platform\n");
+ }
+#endif
+
+ priv->screen_no = xf86SetIntOption(local->options, "ScreenNo", -1);
+ if (priv->screen_no != -1) {
+ xf86Msg(X_CONFIG, "%s: attached screen number %d\n", dev->identifier,
+ priv->screen_no);
+ }
+
+ if (xf86SetBoolOption(local->options, "KeepShape", 0)) {
+ priv->flags |= KEEP_SHAPE_FLAG;
+ xf86Msg(X_CONFIG, "%s: keeps shape\n", dev->identifier);
+ }
+
+ priv->topX = xf86SetIntOption(local->options, "TopX", 0);
+ if (priv->topX != 0) {
+ xf86Msg(X_CONFIG, "%s: top x = %d\n", dev->identifier, priv->topX);
+ }
+ priv->topY = xf86SetIntOption(local->options, "TopY", 0);
+ if (priv->topY != 0) {
+ xf86Msg(X_CONFIG, "%s: top x = %d\n", dev->identifier, priv->topY);
+ }
+ priv->bottomX = xf86SetIntOption(local->options, "BottomX", 0);
+ if (priv->bottomX != 0) {
+ xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier,
+ priv->bottomX);
+ }
+ priv->bottomY = xf86SetIntOption(local->options, "BottomY", 0);
+ if (priv->bottomY != 0) {
+ xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier,
+ priv->bottomY);
+ }
+ priv->serial = xf86SetIntOption(local->options, "Serial", 0);
+ if (priv->bottomY != 0) {
+ xf86Msg(X_CONFIG, "%s: serial number = %u\n", dev->identifier,
+ priv->serial);
+ }
+ common->wcmThreshold = xf86SetIntOption(local->options, "Threshold", common->wcmThreshold);
+ if (common->wcmThreshold != INVALID_THRESHOLD) {
+ xf86Msg(X_CONFIG, "%s: threshold = %d\n", dev->identifier,
+ common->wcmThreshold);
+ }
+ common->wcmMaxX = xf86SetIntOption(local->options, "MaxX", common->wcmMaxX);
+ if (common->wcmMaxX != 0) {
+ xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier,
+ common->wcmMaxX);
+ }
+ common->wcmMaxY = xf86SetIntOption(local->options, "MaxY", common->wcmMaxY);
+ if (common->wcmMaxY != 0) {
+ xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier,
+ common->wcmMaxY);
+ }
+ common->wcmMaxZ = xf86SetIntOption(local->options, "MaxZ", common->wcmMaxZ);
+ if (common->wcmMaxZ != DEFAULT_MAXZ) {
+ xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier,
+ common->wcmMaxZ);
+ }
+ common->wcmResolX = xf86SetIntOption(local->options, "ResolutionX", common->wcmResolX);
+ if (common->wcmResolX != 0) {
+ xf86Msg(X_CONFIG, "%s: resol x = %d\n", dev->identifier,
+ common->wcmResolX);
+ }
+ common->wcmResolY = xf86SetIntOption(local->options, "ResolutionY", common->wcmResolY);
+ if (common->wcmResolY != 0) {
+ xf86Msg(X_CONFIG, "%s: resol x = %d\n", dev->identifier,
+ common->wcmResolY);
+ }
+ common->wcmResolZ = xf86SetIntOption(local->options, "ResolutionZ", common->wcmResolZ);
+ if (common->wcmResolZ != 0) {
+ xf86Msg(X_CONFIG, "%s: resol x = %d\n", dev->identifier,
+ common->wcmResolZ);
+ }
+
+ {
+ int val;
+ val = xf86SetIntOption(local->options, "BaudRate", 0);
+
+ switch(val) {
+ case 38400:
+ common->wcmLinkSpeed = 38400;
+ break;
+ case 19200:
+ common->wcmLinkSpeed = 19200;
+ break;
+ case 9600:
+ common->wcmLinkSpeed = 9600;
+ break;
+ default:
+ xf86Msg(X_ERROR, "%s: Illegal speed value (must be 9600 or 19200 or 38400).", dev->identifier);
+ break;
+ }
+ if (xf86Verbose)
+ xf86Msg(X_CONFIG, "%s: serial speed %u\n", dev->identifier,
+ val);
+ }
+ /* mark the device configured */
+ local->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
+
+ /* return the LocalDevice */
+ return (local);
+
+ SetupProc_fail:
+ if (common)
+ xfree(common);
+ if (priv)
+ xfree(priv);
+ if (local)
+ xfree(local);
+ return NULL;
+}
+
+#ifdef XFree86LOADER
+static
+#endif
+InputDriverRec WACOM = {
+ 1, /* driver version */
+ "wacom", /* driver name */
+ NULL, /* identify */
+ xf86WcmInit, /* pre-init */
+ xf86WcmUninit, /* un-init */
+ NULL, /* module */
+ 0 /* ref count */
+};
+
+/*
+ ***************************************************************************
+ *
+ * Dynamic loading functions
+ *
+ ***************************************************************************
+ */
+#ifdef XFree86LOADER
+/*
+ * xf86WcmUnplug --
+ *
+ * called when the module subsection is found in XF86Config
+ */
+static void
+xf86WcmUnplug(pointer p)
+{
+ DBG(1, ErrorF("xf86WcmUnplug\n"));
+}
+
+/*
+ * xf86WcmPlug --
+ *
+ * called when the module subsection is found in XF86Config
+ */
+static pointer
+xf86WcmPlug(pointer module,
+ pointer options,
+ int *errmaj,
+ int *errmin)
+{
+ xf86Msg(X_INFO, "Wacom driver level: %s\n", identification+strlen("$Identification: "));
+
+ xf86AddInputDriver(&WACOM, module, 0);
+
+ return module;
+}
+
+static XF86ModuleVersionInfo xf86WcmVersionRec =
+{
+ "wacom",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_XINPUT,
+ ABI_XINPUT_VERSION,
+ MOD_CLASS_XINPUT,
+ {0, 0, 0, 0} /* signature, to be patched into the file by */
+ /* a tool */
+};
+
+XF86ModuleData wacomModuleData = {&xf86WcmVersionRec,
+ xf86WcmPlug,
+ xf86WcmUnplug};
+
+#endif /* XFree86LOADER */
+#endif /* XFREE86_V4 */
+
+/*
+ * Local variables:
+ * change-log-default-name: "~/xinput.log"
+ * c-file-style: "bsd"
+ * End:
+ */
+/* end of xf86Wacom.c */