diff options
author | jjoganic <jjoganic> | 2002-12-23 02:44:28 +0000 |
---|---|---|
committer | jjoganic <jjoganic> | 2002-12-23 02:44:28 +0000 |
commit | ba23e0362c2fd1aa72bea910812382d2bb9ed229 (patch) | |
tree | a6ce0383df6b9a1b459af8a6cbc3a70ad30f682b | |
download | xf86-input-wacom-ba23e0362c2fd1aa72bea910812382d2bb9ed229.tar.gz |
Initial revision
-rw-r--r-- | AUTHORS | 4 | ||||
-rw-r--r-- | ChangeLog | 43 | ||||
-rw-r--r-- | GPL | 340 | ||||
-rw-r--r-- | LGPL | 504 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | NEWS | 10 | ||||
-rw-r--r-- | README | 96 | ||||
-rwxr-xr-x | bootstrap | 4 | ||||
-rw-r--r-- | configure.in | 262 | ||||
-rw-r--r-- | docs/docs.html | 2217 | ||||
-rw-r--r-- | docs/docs.txt | 2221 | ||||
-rw-r--r-- | src/Makefile.am | 105 | ||||
-rw-r--r-- | src/hid-core.c | 1386 | ||||
-rw-r--r-- | src/hid-input.c | 434 | ||||
-rw-r--r-- | src/hiddev.c | 686 | ||||
-rw-r--r-- | src/usbmouse.c | 230 | ||||
-rw-r--r-- | src/wacdump.c | 716 | ||||
-rw-r--r-- | src/wacom.c | 690 | ||||
-rw-r--r-- | src/wacscrn.c | 39 | ||||
-rw-r--r-- | src/wacscrn.h | 32 | ||||
-rw-r--r-- | src/wacserial.c | 966 | ||||
-rw-r--r-- | src/wacserial.h | 115 | ||||
-rw-r--r-- | src/wcm-beta.c | 618 | ||||
-rw-r--r-- | src/wcm-beta.h | 67 | ||||
-rw-r--r-- | src/xf86Wacom.c | 4070 |
25 files changed, 15857 insertions, 0 deletions
@@ -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 @@ -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. @@ -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 @@ -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. @@ -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> <A HREF="/wacom/index.php/id/intro">NEXT</A> <A HREF="/wacom/index.php/toc">INDEX</A> <A HREF="/wacom/index.php/all"><B>ALL</B></A> </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: </TD> +<TD>2002-12-22 (Sunday)</TD></TR> +<TR><TD ALIGN=RIGHT>Status: </TD> +<TD>Moving over to SourceForge, and testing new serial driver. +</TD></TR> +<TR><TD ALIGN=RIGHT>Estimated Time: </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 <vojtech@suse.cz> +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 <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: <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 <vojtech@suse.cz> +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 <vojtech@suse.cz> +</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 <lepied@xfree86.org> + +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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><BR> +<BLOCKQUOTE> +* Added detail on "Threshold" parameter.<BR> +* Included man page. +</BLOCKQUOTE> +<LI>2002-11-24 - John E. Joganic <jej@j-arkadia.com><BR> +<BLOCKQUOTE> +* Updated docs for version 0.2.0.<BR> +* Package uses configure-based scripts.<BR> +</BLOCKQUOTE> +<LI>2002-11-14 - John E. Joganic <jej@j-arkadia.com><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 <jej@j-arkadia.com><BR> +<BLOCKQUOTE> +* wacom.c - fixed ranges for intuos2 6x8<BR> +</BLOCKQUOTE> + +<LI>2002-11-10 - John E. Joganic <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <jej@j-arkadia.com><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 <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. + + + +<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: + +<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. +</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 */ |