From 563dae29cd97e8ab1428a4827bdef557ee6fd235 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 8 Oct 2008 23:13:06 +0200 Subject: Initial import of bcm5974-0.42. Changelog from the absolute beginning: bcm5974 (0.42) unstable; urgency=low * First button click registered. * Hooks pre/post-reset added. -- Henrik Rydberg Tue, 01 Jul 2008 20:59:39 +0200 bcm5974 (0.41) unstable; urgency=low * The reset_resume kernel function added. -- Henrik Rydberg Sun, 29 Jun 2008 20:59:39 +0200 bcm5974 (0.4) unstable; urgency=low * Both finger pressure and width reported. -- Henrik Rydberg Sun, 29 Jun 2008 18:59:39 +0200 bcm5974 (0.31) unstable; urgency=low * Administrative changes for kernel.org. -- Henrik Rydberg Fri, 27 Jun 2008 20:59:39 +0200 bcm5974 (0.3) unstable; urgency=low * Explicit device structure, use all packages. -- Henrik Rydberg Fri, 27 Jun 2008 18:59:39 +0200 bcm5974 (0.23) unstable; urgency=low * Finger release not reported properly. -- Henrik Rydberg Fri, 27 Jun 2008 17:59:39 +0200 bcm5974 (0.22) unstable; urgency=low * ABS coordinates more stable with synaptics. -- Henrik Rydberg Thu, 26 Jun 2008 20:59:39 +0200 bcm5974 (0.21) unstable; urgency=low * Fixed the relative coordinate scaling. -- Henrik Rydberg Wed, 25 Jun 2008 20:59:39 +0200 bcm5974 (0.2) unstable; urgency=low * Misinterpreted pressure input, noise reduction. -- Henrik Rydberg Wed, 25 Jun 2008 20:59:39 +0200 bcm5974 (0.1) unstable; urgency=low * First version. -- Henrik Rydberg Tue, 24 Jun 2008 20:59:39 +0200 --- LICENSE | 280 +++++++++++++++++++++ Makefile | 5 + bcm5974-diagnostics | 203 ++++++++++++++++ bcm5974.c | 689 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1177 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100755 bcm5974-diagnostics create mode 100644 bcm5974.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c7aea18 --- /dev/null +++ b/LICENSE @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..74335c8 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for USB Network drivers +# + +obj-m := bcm5974.o diff --git a/bcm5974-diagnostics b/bcm5974-diagnostics new file mode 100755 index 0000000..fa6a4ef --- /dev/null +++ b/bcm5974-diagnostics @@ -0,0 +1,203 @@ +#!/bin/bash +# +# bcm5943-diagnostics - troubleshooting the BCM5974 setup +# +# Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +DRIVER=bcm5974 + +echo ----------------------------------------------------------------------- + +# What kernel version am I running? +KERNEL=`uname -r` +echo \* Kernel version: $KERNEL + +# What synaptics version is installed (debian)? +if [ -x /usr/bin/dpkg-query ]; then + SYNAPTICS=`/usr/bin/dpkg-query -W -f='${Version}\n' xserver-xorg-input-synaptics` + echo \* Synaptics version: $SYNAPTICS +else + echo \* Synaptics version: UNKNOWN - dpkg not found +fi + +# What USB devices of interest can we find? +if [ -x /usr/sbin/lsusb ]; then + LSUSB=`lsusb|grep -c 05ac:022` + echo \* USB device: $LSUSB +else + echo \* USB device: UNKNOWN - lsusb not found +fi + +# Do we have the module intalled? +MODLIB=/lib/modules/$KERNEL/kernel/drivers/input/mouse/$DRIVER.ko +if [ -f $MODLIB ]; then + echo \* $MODLIB: exists +else + echo \* $MODLIB: does NOT exist + echo Copy the file $DRIVER.ko to $MODLIB + exit 1 +fi + +# Do we have the module configured? +MODULES=/etc/modules +if [ -f $MODULES ]; then + if (grep -q $DRIVER $MODULES); then + echo \* $MODULES: has $DRIVER + else + echo \* $MODULES: does NOT have $DRIVER + echo Add this line to $MODULES: + echo $DRIVER + exit 1 + fi +else + echo \* $MODULES: file not found + exit 1 +fi + +# Do we have the right quirks? +MODPROBE=/etc/modprobe.d/options +if [ -f $MODPROBE ]; then + MODPROBEOPTS=`grep quirks= $MODPROBE|grep -v ^#|grep 0x05ac:0x02` + if [ "$MODPROBEOPTS" != "" ]; then + echo \* $MODPROBE: has quirks + else + echo \* $MODPROBE: does NOT have quirks + echo Add this line to $MODPROBE: + echo -n options usbhid quirks= + (lsusb|grep -q 05ac:022) && echo 0x05ac:0x0223:0x00020800,0x05ac:0x0224:0x00024800,0x05ac:0x0225:0x00020800 + (lsusb|grep -q 05ac:023) && echo 0x05ac:0x0230:0x00020800,0x05ac:0x0231:0x00024800,0x05ac:0x0232:0x00020800 + exit 1 + fi +else + echo \* $MODPROBE: file not found + exit 1 +fi + +# Was the $DRIVER module loaded? +DMESG=`lsmod|grep $DRIVER` +if [ "$DMESG" != "" ]; then + echo \* $DRIVER: module is loaded +else + echo \* $DRIVER: module is NOT loaded + echo Add this line to /etc/modules: + echo "$DRIVER" + exit 1 +fi + +# Was the $DRIVER properly initialized? +WELLSPRING=`dmesg|grep $DRIVER|grep Wellspring` +if [ "$WELLSPRING" != "" ]; then + echo \* $DRIVER: Wellspring was initialized +else + echo \* $DRIVER: Wellspring was NOT initialized + exit 1 +fi + +# Do we have synaptics working properly? +typeset -i SYNLINES=`synclient -l|wc -l` +if [ $SYNLINES -gt 10 ]; then + echo \* synaptics: configuration readable +else + echo \* synaptics: configuration NOT readable + exit 1 +fi + +# Do we have a working synaptics configuration? +DIFF=/tmp/synclient-output.diff.$$ +TMP=/tmp/synclient-output.txt.$$ +synclient -l | sort > $TMP +diff -u - $TMP > $DIFF < +#include +#include +#include +#include +#include +#include + +#define APPLE_VENDOR_ID 0x05AC + +/* MacbookAir BCM5974, aka wellspring */ + +#define ATP_WELLSPRING_ANSI 0x0223 +#define ATP_WELLSPRING_ISO 0x0224 +#define ATP_WELLSPRING_JIS 0x0225 +#define ATP_WELLSPRING2_ANSI 0x0230 +#define ATP_WELLSPRING2_ISO 0x0231 +#define ATP_WELLSPRING2_JIS 0x0232 + +#define ATP_DEVICE(prod) { \ + .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_PROTOCOL), \ + .idVendor = APPLE_VENDOR_ID, \ + .idProduct = (prod), \ + .bInterfaceClass = USB_INTERFACE_CLASS_HID, \ + .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \ + } + +/* table of devices that work with this driver */ +static const struct usb_device_id atp_table [] = { + /* MacbookAir1.1 */ + ATP_DEVICE(ATP_WELLSPRING_ANSI), + ATP_DEVICE(ATP_WELLSPRING_ISO), + ATP_DEVICE(ATP_WELLSPRING_JIS), + + /* MacbookProPenryn */ + ATP_DEVICE(ATP_WELLSPRING2_ANSI), + ATP_DEVICE(ATP_WELLSPRING2_ISO), + ATP_DEVICE(ATP_WELLSPRING2_JIS), + + /* Terminating entry */ + {} +}; +MODULE_DEVICE_TABLE(usb, atp_table); + +struct atp_params_t { + int devmin; /* device minimum reading */ + int devmax; /* device maximum reading */ + int min; /* logical minimum reading */ + int max; /* logical maximum reading */ + int fuzz; /* reading noise value */ + int flat; /* zero */ +}; + +struct atp_config_t { + int ansi, iso, jis; /* the product id of this device */ + int bt_ep; /* the endpoint of the button interface */ + int bt_datalen; /* data length of the button interface */ + int tp_ep; /* the endpoint of the trackpad interface */ + int tp_datalen; /* data length of the trackpad interface */ + struct atp_params_t x; /* horizontal limits */ + struct atp_params_t y; /* vertical limits */ + struct atp_params_t p; /* finger pressure limits */ + struct atp_params_t w; /* finger width limits */ +}; + +/* trackpad header structure */ +struct tp_header_t { + u8 unknown1[16]; /* constants, timers, etc */ + u8 nfinger; /* number of fingers on trackpad */ + u8 unknown2[9]; /* constants, timers, etc */ +}; + +/* trackpad finger structure */ +struct tp_finger_t { + __le16 origin; /* left/right origin? */ + __le16 abs_x; /* absolute x coodinate */ + __le16 abs_y; /* absolute y coodinate */ + __le16 rel_x; /* relative x coodinate */ + __le16 rel_y; /* relative y coodinate */ + __le16 size_major; /* finger size, major axis? */ + __le16 size_minor; /* finger size, minor axis? */ + __le16 orientation; /* 16384 when point, else 15 bit angle */ + __le16 force_major; /* trackpad force, major axis? */ + __le16 force_minor; /* trackpad force, minor axis? */ + __le16 unused[3]; /* zeros */ + __le16 multi; /* one finger: varies, more fingers: constant */ +}; + +/* trackpad data structure */ +struct tp_data_t { + struct tp_header_t header; + struct tp_finger_t finger[16]; +}; + +/* device constants */ +static const struct atp_config_t atp_config_table[] = { + { + ATP_WELLSPRING_ANSI, + ATP_WELLSPRING_ISO, + ATP_WELLSPRING_JIS, + 0x84, 4, + 0x81, sizeof(struct tp_data_t), + {-4824, 5342, 0, 1280, 16, 0}, + {-172, 5820, 0, 800, 16, 0}, + {0, 256, 0, 256, 16, 0}, + {0, 2048, 0, 256, 16, 0} + }, + { + ATP_WELLSPRING2_ANSI, + ATP_WELLSPRING2_ISO, + ATP_WELLSPRING2_JIS, + 0x84, 4, + 0x81, sizeof(struct tp_data_t), + {-4824, 5342, 0, 1280, 16, 0}, + {-172, 5820, 0, 800, 16, 0}, + {0, 256, 0, 256, 16, 0}, + {0, 2048, 0, 256, 16, 0} + }, + {} +}; + +static inline +const struct atp_config_t *atp_product_config(struct usb_device *udev) +{ + u16 id = le16_to_cpu(udev->descriptor.idProduct); + const struct atp_config_t *config; + for (config = atp_config_table; config->ansi; ++config) + if (config->ansi == id || config->iso == id || config->jis == id) + return config; + return atp_config_table; +} + +/* Wellspring initialization constants */ +#define ATP_WELLSPRING_MODE_READ_REQUEST_ID 1 +#define ATP_WELLSPRING_MODE_WRITE_REQUEST_ID 9 +#define ATP_WELLSPRING_MODE_REQUEST_VALUE 0x300 +#define ATP_WELLSPRING_MODE_REQUEST_INDEX 0 +#define ATP_WELLSPRING_MODE_VENDOR_VALUE_1 0x01 +#define ATP_WELLSPRING_MODE_VENDOR_VALUE_2 0x05 + +#define dprintk(format, a...) \ + { if (debug) printk(KERN_DEBUG format, ##a); } + +MODULE_AUTHOR("Henrik Rydberg"); +MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver"); +MODULE_LICENSE("GPL"); + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activate debugging output"); + +static int atp_wellspring_init(struct usb_device *udev) +{ + const struct atp_config_t *cfg = atp_product_config(udev); + char *data = kmalloc(8, GFP_KERNEL); + int size; + + /* reset button endpoint */ + if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + 0, cfg->bt_ep, NULL, 0, 5000)) { + err("Could not reset button endpoint"); + goto error; + } + + /* reset trackpad endpoint */ + if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + 0, cfg->tp_ep, NULL, 0, 5000)) { + err("Could not reset trackpad endpoint"); + goto error; + } + + /* read configuration */ + size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + ATP_WELLSPRING_MODE_READ_REQUEST_ID, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ATP_WELLSPRING_MODE_REQUEST_VALUE, + ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000); + + if (size != 8) { + err("Could not do mode read request from device (Wellspring Raw mode)"); + goto error; + } + + /* apply the mode switch */ + data[0] = ATP_WELLSPRING_MODE_VENDOR_VALUE_1; + data[1] = ATP_WELLSPRING_MODE_VENDOR_VALUE_2; + + /* write configuration */ + size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + ATP_WELLSPRING_MODE_WRITE_REQUEST_ID, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ATP_WELLSPRING_MODE_REQUEST_VALUE, + ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000); + + if (size != 8) { + err("Could not do mode write request to device (Wellspring Raw mode)"); + goto error; + } + + printk(KERN_INFO "bcm5974: Wellspring mode initialized.\n"); + + kfree(data); + return 0; + + error: + kfree(data); + return -EIO; +} + +/* Structure to hold all of our device specific stuff */ +struct atp { + char phys[64]; + struct usb_device *udev; /* usb device */ + struct input_dev *input; /* input dev */ + struct atp_config_t cfg; /* device configuration */ + int open; /* >0: open, else closed */ + int suspended; /* >0: suspended, else open */ + struct urb *bt_urb; /* button usb request block */ + signed char *bt_data; /* button transferred data */ + unsigned bt_state; /* current button state */ + struct urb *tp_urb; /* trackpad usb request block */ + signed char *tp_data; /* trackpad transferred data */ + unsigned tp_valid; /* are the trackpad sensors valid ? */ +}; + +static inline int raw2int(__le16 x) +{ + return (short)le16_to_cpu(x); +} + +static inline int int2scale(const struct atp_params_t *p, int x) +{ + return ((p->max-p->min)*x)/(p->devmax-p->devmin); +} + +/** + * all value ranges, both device and logical, are assumed to be [a,b). + */ +static inline int int2bound(const struct atp_params_t *p, int x) +{ + int s = p->min+int2scale(p, x); + return s < p->min?p->min:s >= p->max?p->max-1:s; +} + +/** + * check quality of reading. + * -1: bad data + * 0: ignore this reading + * 1: good reading + */ +static int compute_quality(const unsigned char *data, int size) +{ + if (size < 26 || (size-26)%28 != 0) + return -1; + + return 1; +} + +/** + * convert raw data to synaptics sensor output. + * returns the number of fingers on the trackpad, + * or a negative number in vase of bad data. + */ +static int compute_movement(int *abs_x, int *abs_y, + int *rel_x, int *rel_y, + int *pressure, int *width, + struct atp_config_t *cfg, + const unsigned char *data, int size) +{ + const int nfinger = (size-26)/28; + const struct tp_data_t *tp = (struct tp_data_t *) data; + + if (nfinger) { + *abs_x = int2bound(&cfg->x, raw2int(tp->finger->abs_x) - cfg->x.devmin); + *abs_y = int2bound(&cfg->y, cfg->y.devmax - raw2int(tp->finger->abs_y)); + *rel_x = int2scale(&cfg->x, raw2int(tp->finger->rel_x)); + *rel_y = int2scale(&cfg->y, -raw2int(tp->finger->rel_y)); + *pressure = int2bound(&cfg->p, raw2int(tp->finger->force_major)); + *width = int2bound(&cfg->w, raw2int(tp->finger->size_major)); + } else { + *abs_x = 0; + *abs_y = 0; + *rel_x = 0; + *rel_y = 0; + *pressure = 0; + *width = 0; + } + + /* report zero fingers for zero pressure */ + return *pressure > 0?nfinger:0; +} + +static void atp_button(struct urb *urb) +{ + struct atp *dev = urb->context; + int button, retval; + + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + dbg("bcm5974: button urb shutting down: %d", urb->status); + return; + default: + dbg("bcm5974: button urb status: %d", urb->status); + goto exit; + } + + /* drop incomplete datasets */ + if (dev->bt_urb->actual_length != 4) { + dprintk("bcm5974: incomplete button package (first byte: %d, length: %d)\n", + (int)dev->bt_data[0], dev->bt_urb->actual_length); + goto exit; + } + + button = dev->bt_data[1]; + + /* only report button state changes */ + if (button != dev->bt_state) { + input_report_key(dev->input, BTN_LEFT, button); + input_sync(dev->input); + } + + dev->bt_state = button; + + exit: + retval = usb_submit_urb(dev->bt_urb, GFP_ATOMIC); + if (retval) + err("bcm5974: button urb failed: %d", retval); +} + +static void atp_trackpad(struct urb *urb) +{ + struct atp *dev = urb->context; + int abs_x, abs_y, rel_x, rel_y, pressure, width; + int quality, nfinger, retval; + + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + dbg("bcm5974: trackpad urb shutting down: %d", urb->status); + return; + default: + dbg("bcm5974: trackpad urb status: %d", urb->status); + goto exit; + } + + /* first sample data ignored */ + if (!dev->tp_valid) { + dev->tp_valid = 1; + goto exit; + } + + /* determine quality of reading */ + quality = compute_quality(dev->tp_data, dev->tp_urb->actual_length); + + /* drop incomplete datasets */ + if (quality < 0) { + dprintk("bcm5974: incomplete trackpad package " + "(first byte: %d, length: %d)\n", + (int)dev->tp_data[0], dev->tp_urb->actual_length); + goto exit; + } + + /* drop poor quality readings */ + if (quality == 0) + goto exit; + + nfinger = compute_movement(&abs_x, &abs_y, + &rel_x, &rel_y, + &pressure, &width, + &dev->cfg, + dev->tp_data, dev->tp_urb->actual_length); + + if (debug > 1) { + printk(KERN_DEBUG "bcm5974: x: %04d y: %04d " + "dx: %3d dy: %3d p: %3d w: %3d\n", + abs_x, abs_y, rel_x, rel_y, pressure, width); + } + + input_report_abs(dev->input, ABS_X, abs_x); + input_report_abs(dev->input, ABS_Y, abs_y); + input_report_abs(dev->input, ABS_PRESSURE, pressure); + input_report_abs(dev->input, ABS_TOOL_WIDTH, width); + input_report_key(dev->input, BTN_TOOL_FINGER, nfinger == 1); + input_report_key(dev->input, BTN_TOOL_DOUBLETAP, nfinger == 2); + input_report_key(dev->input, BTN_TOOL_TRIPLETAP, nfinger > 2); + input_sync(dev->input); + + exit: + retval = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); + if (retval) + err("bcm5974: trackpad urb failed: %d", retval); +} + +static int atp_open(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + if (!dev->open) { + if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC)) + goto error; + if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC)) + goto err_free_bt_urb; + } + + dev->open = 1; + dev->suspended = 0; + dev->tp_valid = 0; + + return 0; + + err_free_bt_urb: + usb_free_urb(dev->bt_urb); + error: + return -EIO; +} + +static void atp_close(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + if (dev->open && !dev->suspended) { + usb_kill_urb(dev->tp_urb); + usb_kill_urb(dev->bt_urb); + } + + dev->open = 0; + dev->suspended = 0; +} + +static int atp_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + int error = -ENOMEM; + struct usb_device *udev = interface_to_usbdev(iface); + const struct atp_config_t *cfg; + struct atp *dev; + struct input_dev *input_dev; + + printk(KERN_INFO "bcm5974: Probing device...\n"); + + /* find the product index */ + cfg = atp_product_config(udev); + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(struct atp), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!dev || !input_dev) { + err("Out of memory"); + goto err_free_devs; + } + + dev->udev = udev; + dev->input = input_dev; + dev->cfg = *cfg; + + /* switch to raw sensor mode */ + if (atp_wellspring_init(udev)) + goto err_free_devs; + + dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->bt_urb) + goto err_free_devs; + + dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->tp_urb) + goto err_free_bt_urb; + + dev->bt_data = usb_buffer_alloc(dev->udev, + dev->cfg.bt_datalen, + GFP_KERNEL, + &dev->bt_urb->transfer_dma); + if (!dev->bt_data) + goto err_free_urb; + + dev->tp_data = usb_buffer_alloc(dev->udev, + dev->cfg.tp_datalen, + GFP_KERNEL, + &dev->tp_urb->transfer_dma); + if (!dev->tp_data) + goto err_free_bt_buffer; + + usb_fill_int_urb(dev->bt_urb, udev, + usb_rcvintpipe(udev, cfg->bt_ep), + dev->bt_data, dev->cfg.bt_datalen, + atp_button, dev, 1); + + usb_fill_int_urb(dev->tp_urb, udev, + usb_rcvintpipe(udev, cfg->tp_ep), + dev->tp_data, dev->cfg.tp_datalen, + atp_trackpad, dev, 1); + + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input_dev->name = "bcm5974"; + input_dev->phys = dev->phys; + usb_to_input_id(dev->udev, &input_dev->id); + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, dev); + + input_dev->open = atp_open; + input_dev->close = atp_close; + + set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_X, + cfg->x.min, cfg->x.max, cfg->x.fuzz, cfg->x.flat); + input_set_abs_params(input_dev, ABS_Y, + cfg->y.min, cfg->y.max, cfg->y.fuzz, cfg->y.flat); + input_set_abs_params(input_dev, ABS_PRESSURE, + cfg->p.min, cfg->p.max, cfg->p.fuzz, cfg->p.flat); + input_set_abs_params(input_dev, ABS_TOOL_WIDTH, + cfg->w.min, cfg->w.max, cfg->w.fuzz, cfg->w.flat); + + set_bit(EV_KEY, input_dev->evbit); + set_bit(BTN_TOOL_FINGER, input_dev->keybit); + set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + set_bit(BTN_LEFT, input_dev->keybit); + + error = input_register_device(dev->input); + if (error) + goto err_free_buffer; + + /* save our data pointer in this interface device */ + usb_set_intfdata(iface, dev); + + return 0; + + err_free_buffer: + usb_buffer_free(dev->udev, dev->cfg.tp_datalen, + dev->tp_data, dev->tp_urb->transfer_dma); + err_free_bt_buffer: + usb_buffer_free(dev->udev, dev->cfg.bt_datalen, + dev->bt_data, dev->bt_urb->transfer_dma); + err_free_urb: + usb_free_urb(dev->tp_urb); + err_free_bt_urb: + usb_free_urb(dev->bt_urb); + err_free_devs: + usb_set_intfdata(iface, NULL); + kfree(dev); + input_free_device(input_dev); + return error; +} + +static void atp_disconnect(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + usb_set_intfdata(iface, NULL); + if (dev) { + input_unregister_device(dev->input); + usb_buffer_free(dev->udev, dev->cfg.tp_datalen, + dev->tp_data, dev->tp_urb->transfer_dma); + usb_buffer_free(dev->udev, dev->cfg.bt_datalen, + dev->bt_data, dev->bt_urb->transfer_dma); + usb_free_urb(dev->tp_urb); + usb_free_urb(dev->bt_urb); + kfree(dev); + } + printk(KERN_INFO "bcm5974: disconnected\n"); +} + +static int atp_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct atp *dev = usb_get_intfdata(iface); + + if (dev->open && !dev->suspended++) { + usb_kill_urb(dev->tp_urb); + usb_kill_urb(dev->bt_urb); + } + + return 0; +} + +static int atp_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + if (dev->open && !--dev->suspended) { + if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC)) + goto error; + if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC)) + goto err_free_bt_urb; + } + + dev->tp_valid = 0; + return 0; + + err_free_bt_urb: + usb_free_urb(dev->bt_urb); + error: + return -EIO; +} + +static int atp_reset_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + if (dev->suspended == 1) { + if (atp_wellspring_init(dev->udev)) + printk(KERN_INFO "bcm5974: warning: reset failed\n"); + } + + return atp_resume(iface); +} + +static int atp_pre_reset(struct usb_interface *iface) +{ + return atp_suspend(iface, PMSG_ON); +} + +static int atp_post_reset(struct usb_interface *iface) +{ + return atp_reset_resume(iface); +} + +static struct usb_driver atp_driver = { + .name = "bcm5974", + .probe = atp_probe, + .disconnect = atp_disconnect, + .suspend = atp_suspend, + .resume = atp_resume, + .reset_resume = atp_reset_resume, + .pre_reset = atp_pre_reset, + .post_reset = atp_post_reset, + .id_table = atp_table, +}; + +static int __init atp_init(void) +{ + return usb_register(&atp_driver); +} + +static void __exit atp_exit(void) +{ + usb_deregister(&atp_driver); +} + +module_init(atp_init); +module_exit(atp_exit); -- cgit v1.2.3