From b89270c787f77d24639d898de0b155b86cc28ac5 Mon Sep 17 00:00:00 2001 From: Joel Matteotti Date: Wed, 23 Apr 2014 12:24:45 +0200 Subject: [PATCH] - Migration of projet to github --- AUTHORS | 8 + COPYING | 340 ++++++ README.md | 11 +- doc/Notes.txt | 8 + doc/TuxSSV2-Tuxware-Libtuxdriver.odt | Bin 0 -> 53692 bytes include/tux_driver.h | 245 +++++ include/tux_driver.pas | 233 +++++ include/tux_driver.py | 404 ++++++++ src/log.c | 255 +++++ src/log.h | 63 ++ src/svnrev.tmpl.h | 37 + src/threading_uniform.h | 54 + src/tux_audio.c | 76 ++ src/tux_audio.h | 37 + src/tux_battery.c | 128 +++ src/tux_battery.h | 47 + src/tux_cmd_parser.c | 1434 ++++++++++++++++++++++++++ src/tux_cmd_parser.h | 61 ++ src/tux_descriptor.c | 66 ++ src/tux_descriptor.h | 67 ++ src/tux_driver.c | 568 ++++++++++ src/tux_error.c | 65 ++ src/tux_error.h | 55 + src/tux_eyes.c | 192 ++++ src/tux_eyes.h | 47 + src/tux_firmware.c | 650 ++++++++++++ src/tux_firmware.h | 121 +++ src/tux_flippers.c | 196 ++++ src/tux_flippers.h | 48 + src/tux_hid_unix.c | 269 +++++ src/tux_hid_unix.h | 46 + src/tux_hid_win32.c | 240 +++++ src/tux_hid_win32.h | 47 + src/tux_hw_cmd.h | 96 ++ src/tux_hw_status.c | 657 ++++++++++++ src/tux_hw_status.h | 365 +++++++ src/tux_id.c | 89 ++ src/tux_id.h | 37 + src/tux_leds.c | 619 +++++++++++ src/tux_leds.h | 95 ++ src/tux_light.c | 62 ++ src/tux_light.h | 26 + src/tux_misc.c | 210 ++++ src/tux_misc.h | 55 + src/tux_mouth.c | 193 ++++ src/tux_mouth.h | 49 + src/tux_movements.c | 411 ++++++++ src/tux_movements.h | 63 ++ src/tux_pong.c | 112 ++ src/tux_pong.h | 27 + src/tux_sound_flash.c | 778 ++++++++++++++ src/tux_sound_flash.h | 53 + src/tux_spinning.c | 232 +++++ src/tux_spinning.h | 52 + src/tux_sw_status.c | 672 ++++++++++++ src/tux_sw_status.h | 127 +++ src/tux_types.h | 238 +++++ src/tux_usb.c | 660 ++++++++++++ src/tux_usb.h | 202 ++++ src/tux_user_inputs.c | 341 ++++++ src/tux_user_inputs.h | 87 ++ src/version.h | 39 + test/Makefile.unix | 60 ++ test/Makefile.win32 | 67 ++ test/compile.bat | 4 + test/main.c | 223 ++++ test/tada.wav | Bin 0 -> 2736 bytes test/test_macro.txt | 9 + unix/Makefile | 78 ++ win32/Makefile | 82 ++ win32/libtuxdriver.dev | 602 +++++++++++ 71 files changed, 13887 insertions(+), 3 deletions(-) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 doc/Notes.txt create mode 100644 doc/TuxSSV2-Tuxware-Libtuxdriver.odt create mode 100644 include/tux_driver.h create mode 100644 include/tux_driver.pas create mode 100644 include/tux_driver.py create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/svnrev.tmpl.h create mode 100644 src/threading_uniform.h create mode 100644 src/tux_audio.c create mode 100644 src/tux_audio.h create mode 100644 src/tux_battery.c create mode 100644 src/tux_battery.h create mode 100644 src/tux_cmd_parser.c create mode 100644 src/tux_cmd_parser.h create mode 100644 src/tux_descriptor.c create mode 100644 src/tux_descriptor.h create mode 100644 src/tux_driver.c create mode 100644 src/tux_error.c create mode 100644 src/tux_error.h create mode 100644 src/tux_eyes.c create mode 100644 src/tux_eyes.h create mode 100644 src/tux_firmware.c create mode 100644 src/tux_firmware.h create mode 100644 src/tux_flippers.c create mode 100644 src/tux_flippers.h create mode 100644 src/tux_hid_unix.c create mode 100644 src/tux_hid_unix.h create mode 100644 src/tux_hid_win32.c create mode 100644 src/tux_hid_win32.h create mode 100644 src/tux_hw_cmd.h create mode 100644 src/tux_hw_status.c create mode 100644 src/tux_hw_status.h create mode 100644 src/tux_id.c create mode 100644 src/tux_id.h create mode 100644 src/tux_leds.c create mode 100644 src/tux_leds.h create mode 100644 src/tux_light.c create mode 100644 src/tux_light.h create mode 100644 src/tux_misc.c create mode 100644 src/tux_misc.h create mode 100644 src/tux_mouth.c create mode 100644 src/tux_mouth.h create mode 100644 src/tux_movements.c create mode 100644 src/tux_movements.h create mode 100644 src/tux_pong.c create mode 100644 src/tux_pong.h create mode 100644 src/tux_sound_flash.c create mode 100644 src/tux_sound_flash.h create mode 100644 src/tux_spinning.c create mode 100644 src/tux_spinning.h create mode 100644 src/tux_sw_status.c create mode 100644 src/tux_sw_status.h create mode 100644 src/tux_types.h create mode 100644 src/tux_usb.c create mode 100644 src/tux_usb.h create mode 100644 src/tux_user_inputs.c create mode 100644 src/tux_user_inputs.h create mode 100644 src/version.h create mode 100644 test/Makefile.unix create mode 100644 test/Makefile.win32 create mode 100644 test/compile.bat create mode 100644 test/main.c create mode 100644 test/tada.wav create mode 100644 test/test_macro.txt create mode 100644 unix/Makefile create mode 100644 win32/Makefile create mode 100644 win32/libtuxdriver.dev diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..056a54a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,8 @@ +libtuxdriver was written by: + + Remi Jocaille + +Contributors: + Frans Meulenbroeks aka eFfeM + Paul Rathgeb + David Bourgeois diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/README.md b/README.md index a4d1aba..5b56bb3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -tuxdriver -========= -New driver for TuxDroid (more infos here: http://forum.tuxdroid-community.org/viewtopic.php?id=205) +libtuxdriver is a shared library to control Tuxdroid. + +For all information about Tuxdroid, please visit: + + http://tuxdroid-community.org/ + http://www.tux-droid.eu/ + + diff --git a/doc/Notes.txt b/doc/Notes.txt new file mode 100644 index 0000000..7932d1e --- /dev/null +++ b/doc/Notes.txt @@ -0,0 +1,8 @@ +Defines: +======== + +USE_MUTEX +LOCK_TUX +USB_DEBUG +USB_IDFRAME +GENERATE_DOC \ No newline at end of file diff --git a/doc/TuxSSV2-Tuxware-Libtuxdriver.odt b/doc/TuxSSV2-Tuxware-Libtuxdriver.odt new file mode 100644 index 0000000000000000000000000000000000000000..5216830a5266ccf86cc6724e2b7941e56ac3d482 GIT binary patch literal 53692 zcmeFZbzD^4w?BS{p=&@ohwhM&ZUh7*1OaJjM!K63=`KM;2}uzFX+aSIX$7POr8}fy z=H)6dn$!qwFgVP)ZoaB=4McDCkovG726@VU6!I9t0|c{$lQd-8eO_;RY9aqR&ce#d z#?c0RjEg(Jm6yBw)$G1bj!-~b9qVe*0NT}m;Pv;*p#<;#>tk&lT`W9peqYSh_npkqq@%pOY(N^S4$=|c2pb=e{tD78 zj$Td(kUjuuQYRY=4*)>NxoY>cv9bs0E9v9A-_=nBX>rhU@$G)m_)1r_T3>;RR3m~&J$bb)tQ;)dtr-+uoLnuO{ea(Cz8VBv zyAmw}C}R-;aS;(-K|WCWe_a3N%|Ckm*TmJ<{?=pf&aW+lkbnNO?a#4)wz=d1fEXwi zXwsi;mQMkoJ{$ljC;n_>$prvd7y#7u|Aim&D|NB=^mLWr=lAjP;X~M1@mf(JlNvcWr$KX&aljy`Z@b_Vd}-<1B>qkfq(XfTQ3yWc(iuNxi+JBBMlQRglL z7#F;MH3H~YFBkwW00T$?Du5nf1~@=>B?yQDl7Jkb1gHU8z+J!)FaxXrd%y{B2Yi4) zAOwg2Vu2(e4afv?fC8WdCgor_8AWD$i5FLmi;tcVE1VTa~v5*ug4#mep~27?XgV|xS`KZ5c0z}sGtdp_F*+tX z2|5EhFS;c9Ep$C}D|9#XVDvcjO!SxNb?BeahtOxyx6v;!2ry_dxG^L!R59*h*kSl! zL|~+26k*h2v}255EMgpDVq%hGvSW&3-oiA*e1I8%8HbsLS%LWx^DE{&<^dKK78MpZ zmNb?YmIan4Rs_~ltk+o0Sc6!LSSV~fY&f~!o`*dMS5v6rz=afonO zaU^iGaIA6sa1wC}a2j#?aTak-afxx+aiwwZ;y%C)!Og%e$8E>`j=P74hsT5`j&}zS zf%gzE1Mdx97v3!1G5$4tE__9N6MQfH1pH$BkNDs4_Xr3G*a+kZj0ijl5(r8N+6bly zP=q9ee1xim)`UpHXM}ZxgM>d}I51Y2Jj@K{4@-wt!}?(xL^wojM2bWfM8QPQh#H8- zhz^KJhy{qXh@FVzh+h$R6R%vuy2f@*>6-Pmuxt6(+OEx$KuMTM6iKW|LP-ip+DR5k zF-h4;RY@O^#*&ti_K|LrT_Y1F(9}rix~Q7LAsRR);o#HlOx0?ExJO;|s<<##1JCCOxJ{OchK&m~olKm~ELKGq*Bt zv%pz2SOQr}SteLY5BgGZdlnJ15Dj2E9*p4XeVly{bo zobNW@BffgRpZqNRM*PYAo%|QqZ(Mi0UT}R<;F^G%z$1Z1fn7l^K`X&$g2O@tLdrr& zp|?Ui!ra0(!r8*(H%M-1+=#rc< zr1*RBV+j!nPl+mtElFNU2gz5GD^l!IHc|yr^U_Sx7Sb=IXJi;;?#txL%*ZmzTFB#Ap}&)w#_?S8va15HCs zBUR(OCab2iX1x}mrK**x^+TIo+fDo39n3r0cb?r@(h<-J)aleE(KXk7rF(c+;cnvH zNj(ldFTGZMn7)aAiT;tnO@kDJnS1>Ag6{PiQX3)+-x^^V=^GUp9T+Parx`Dr+%SnS z88u}$^)>A_qc(FiYchwKTbNhh$GC5B|JD673oVO0ivvql%jcHcR*F_ntk$jNtRGvi z+DO}^*eu&h+ossA*h$-^+O68l+NaxZAQTaqh@A(w9^^bYa?o-pcDQsjaIA2`cDnD> z;C#*5!TFO5y^F8QS66P=2-g`m3AZ%2ZFhC|A`gg%smEJSQV>af_Tumg_nP&V@qXrw z^3nIH_9gap_WkU~U7#I>b8zdj}A{Y{E8Qh9wMus65AF4cj z8G;+)5c1{G^+zd>_CpOq8^h?r9)`__tAv+E5JtF1j73UD=0u@KA)@-Cg`%HEU&L6) z^u%6|O^-c`vx@797l_Y@zf7=A=t~qy%ud2ga!wjemQ8+{LY(5CGM}oETKAaYam?fW zH1o9Xbm8=zC%8|%p3G$2&UpKjvrPNUk!K3eDxT9mkA8lXWt}ycEtg%MLzfeq zbCQe59e<(nqArguFFhYK-#dS)K);}~P^_?|h`K1Y=)Bmuc;=lGiwg3J*f+o%uQzLz_bW%M)T>&n#j49|SZkiwlGMi3LF)YK zw(ITdXWkmV{o0_`(Ap^3So@CeUGaOy_s^Qhni4W$2`ubr|cCcmW=1|A5>TvIf=E&Dkz0vV8ld+j`>+zLuj^B1ByeCd4AAZO9 z9`l3fNBR`)RNge_bj6IwOw;Vm+0S#jbCdH{^Xm&93ulXAO9V^l%kbrr6~UGFtIDeb zYsPDf>#pmk8xfnto6mo8{H)oM+xonHZ+mgaedlsFZjWlOcwczG^+5Yz>d@)%^e6^J zg?f1`cHDVlaI$>rbB1-6an5<(c%gpr{nF|3@*gbl4;J_b3;crx{=owOV1a+Iz&}{v zpS-|7d4YfO0{`R%{>cmclNb0WFYy20yujrJK%(ew0~UR)8GI0)_6#bj8rqj10b*?6 zEu;zxAqLQhAy8t-z{tn?I=O#IBOoLoG-JT!~~!q>TlIk1)pYOb z=^KF67%OWVTRVHi0}oFxZy#Sj|Io1Th{&kun3UAVY3WZgo@VCd7Zes1zbtuGRb5kC zSO2!5v9;|}dq-zichBI^@W|-c__vAKx%q{~rR9~?wVmC){e#0J)bR-ffd0nL9~}LK zA5i=dG<0+*I@T3G5Hz1FW{J@;7zHq|$=|`Ua3f(748bN4X1yvJLhnm_tkmn zt7p(ATIH{P8@L98q zr>yqLgi2`(oM^=bsuyThBQg(59M?Qf-xUMr%C1Oi;KX4D)=85{^5BlIM|+X%CMPDZ z3n>N(k<2_{NE7z@Sqofo=>L^iVR+{iwSJDlv_|(@+m&DVm#|5`dNh94dT|%Blf@Vg zkU};ufhv&--o#HwZ}0=-75Z-4Y&N*|AeG_qKv4c|d2qvBo2lR1f569vcw-nywW88!G>GG(!A!6x2wdp^U>VNkVxO{4V33OG?_Me=kUIKCjACLJ$ zuDDwRxw{0;(lwYb8opctc@qz1R*M+Gy_LCs{yNaitDmkY+KJ7v#&i55J^q(o_(vyr zNP$q>%PaZwBD{4U7-bd4S3SvIvW~bxr!D}%J`Z56IzM&fshL#Qc4r7@?jL}hb4FGF z)h_-gEMSTh&t*Z?9=De`_&dGMf2H9bGo37+i&K+BWPj4atTy84a%+wECt24SB$q(q z|7&Kj=a+yJ{Fq!y{1PxvL9UQyz*z^bgNv3nKQzAtQo8dmfidysePeB`AHttVA>s2C z#PYMU*lU?TMsW>4n%>R%a88E2CYKBA(@@wAQn;Ag{i(?MHR-qV4-+JSSD=&vYa|og8rhz0i>%rYYW^_}YP}xdDFNTS!=F)F zjzZx2r1Z@^lAYm^Eu+Ar@0@Xoog&;xEs*dosypf*9Nfk(U>~raZAL>x?O!>2P?cn5 zE+jwiotf?EcDc81=bmuj%F1!#&#VBsFvs&`XKHvX0i`3({&7;G37N}z`a_xFUzYVB zqkp~py_&)g{YH5A6Js0Ej}j`|SmGhxVnAb>`EUqo0Bc(KlRKPL%Bu*4)kd=BA6ja1 zo!h}FC(}>hBmGqIn_O7`Ceq;jSZ(PK1D(gcemF7gGHBtIIqteEj!!@JQCKyk# z=CEZ81?Veg6KPRa?byW%qZ{Sl^KG;&Y3m0K%MAN%`T*XhBttwjgckO zv#!$=G1xj~{H)}equb5Gr-`6^C0vNlZtO%x?6!gTLqkM zAvr(ZaCL6?A#s6ooSL^0KPR|j4R!2UwRgHAxqAGG5K-y>f=y&R&TEhh_qLbMgH zub+gC)!)1?92rI*BLUgqfYSf+29vTjmq29cTm@NOZHZ#D>fpWW`n2kzdgiVYIc=j4 zWjaO=;w2S^7Cwyf-><4k+)@_`hfZb}Ph=!AjUo~J(DBb-zg+_MYm%CxEtn&+8Vm5j zhrB3*!|-j*cL8(-d!`88Tdwf|l-OD?Y*=8Opr;R_GF4>6FR&(Z$Iit7H->9%Yhs`I+f&HeC~_YGX=EdJ7UhMfzu+^*QX zRcvj>IpB(n3Ba1aK)GCS#fEH{k)ax9a!4FCWKHX0W}6qozwMgK87N~QA6@)EEn-Xj^_6$2r|cwJ_%$=CTzX1dXw0BoDCo~}5_ z(DBgQDjz|F#0r0O!L;VD_lBt}S^AB1ez4q`I6R#3`3=CIzojDjB(kl^^*X;>-uLSi z-cM%|w<_{sV}(G_j~8ooMoXy>1#^E~B1n@%v2> zGyt!_6C6u6h#pR8wvnxsDB0Pc$%U^l-tLBtvAwCh&kfQ#c6DVz9AtFc`LOX6%%{)my83u}3hCT6qAVWRhARO6$(bND0isNqrYT zQs`I4OWtPjeET@?v-HT7Y43K}dh>h&QIG0ECio$`KOqgw_G@jKdW$Udr*n?>JBlf` zw6fRt5zib&x<930^g-AczW}~pfFPSwO2hzrWT1SB6Xud}!Nce7WCmhq>cNj&a-_~w zM{7;ZJLwrxb+dhv%NLcSGiLSfg`4;ToRIDTv}_LOKKo2z)@aUlaU1#ATCZ!}1GRcR z^pyN-yUkb@ryYK+yIiKiU2XG~do0Yd;f&eDpi~0JHs0;=mK+6jPhWda8mL^f6_%x>&SxEY7K#(+7it529;VNKWk2cvQr50BKm4m0KTe#W&j_3=1JQZ^T7AfRv^M8d^&tROL>~}LwcU%Hxk-YD$shIk# z9{UUK%k~dGl$k(@?zi*_;t4YJFxca=G7b& zo`%ek_bSto2b>Z@%cn0Q?4kP|?1oG#af?*)CjPp_$89^9J*J|aW(+mYiCGNxpgBj~ z@IuDt=b&CXs5LhX)^Z!4;uz7CBFOI*n7#VAKnZ~OF7`9cJBEI@XBsYJ@{@!U?sRI{*iH7<1&~i z!XCbB7fU}+Opqkp#}BjypmOx)e_Dt?DpeWfG;;|M{>Z{?GuolAcRCyr$t$|$l*f+! z>S>ErT#7wZDp0?!@z^T|%~x|*_Qb~izQw+c?BGN3>8J|<`s>4vEcHY4L&B$cH*0IF z5){_2#ic&-2i14sC3FS;jlVs`K6A9N@Q2Y4)gp5Y4>wYtRrJ{Gdm$eg4h~68S-r_W zbUf*FQlD*GlgKn|TW}aFR2o@)Z>!uPFQ(9P_c?wxq0qXSWvAZjt%zdZBj@fD=Wmk- zMxK{IJhx1*!JgC|Pji^bj>Fs?68c9!zYYXDWYTj}z!KumsgYTs;}(nC!}YJgXed22 zLlOC2h_#`1$0j?zq!+>e%;KdAf5uyOR$8}#T~T~@graEMp8Lpq_)%K(4549=gNMSP zndb7K|A*?Ll}4$F?0g@BrZCQiLR%WQ&Gr3?JE}K4U2BG_L2Sx_syuU;&(bpH>c-!y zLw7Er-p)Opv0Jl;KE6VM*RUCqR1`QMrcb|DSK3OYXsYI74q&}HW2sMUZKN;Vf!zUk zQ<}2Vs4ju@Bj*C^0HzeJOTg%%RFw*X#m?PuJmu4oFfj3lJAf9U+~#^KFnrszM$4!w z@#cJ6E&*fW(;g&j7{ni>m5mW6J$Ua7(bM-js+8yRIjOLAi+}O9ShxyQWhVk9U)2fi& z%9EGSwGaCdfvZ>A1sM+%L4}u{$&5_wDWw^4y7KYbbSK=GuqF)i0Kv_~R`Y|WlbYOC zvr+TS+-<#{X4iwf!_hB1O?U!#=9~iV?1vuiswCoG7`<{coL)KnHb z^=dfIWI%o>W(f!uhAa%VvyiCbcYW=pA@j6&RN#fK4OL->mH|N;+ln&;`G~`bt%O`e zQ3ejfka4(@R49ANC16({xDU`b?cA0L-R{>hMVsQbz~^0T$HmZVO#z_bXK1T${=lG5 zP?c3akk}T{a~WY9D@*J@jm97BW+@+3+c3;Z)bY>!8Rw^?YDhci;}a0yb3d?bs~_C& z(mVDhUyI+@59bCkA@ZB}(1XqKRiy|eM#Z9ZvqVZ+=AAS+)!Z^Yr-~<<32VA)-`Ujd z(1mC19&<0NNGaSj6xeg>bL*}kiF_f}>T+S)5KzD2Z}>iMk?e4@j@Yn_O_ zw|?SzD9W6h>}#?1TFl}n0bm6>M;bH2?ai&2Gj)r+)zW9NB_cniiT$hL_XX{t#i*7! zZtion{ynmIwE8K5Aor z%7*RmxE@#e`rNx$nYC%d0MDKPJ|)wOaxztkt-Zdk1c>}XxIYuWMVdLe%<^TY$gI|^ z(toKQ!;3IK^LiXi`pLij%J*aSA?p&)V(Dowfr@U~ecaoylod+QTa$ZE(qw6A*Hyj= zUw-#CNrlv2+?yT11x|!+o~QHMy-~f9bFCfW*_(<_p8ep(CE(k*yqnAC=hAK-PQBIE zFsMFLpJ`H7$Gvhloc(L$c8*I|Ca~A@%d~EyXy%W$vEnTnvocmi?<@L;o`!AD_?IlN zCQm(-ncWeIEkgCTJ~6VHRoy>D1sf7Q5??gcs%*i`NbE<__}ZSf(zAc~>UEPTz>Y!Y zI*Zn<+lGp8gax?hKcboc*2Xe^<5AQNb8M4OwqL!Tu~S&duBo}FBp!y|2UWTib=pdu z^C(c&BY$m&n8i*&4U^n@V|@du`54mo+k+syHM^6yt6G> zhDF_lQ%Vf>H=4-aM}|i#>_BSH??N~1p~KDgu!vFW=5UJ6546p-@6yHewhF_z3MaOu zz9v9V5-vVzW2Fc8U69ANo0AH!@XcQ{G>mwkh23>OV**w;R|7dJ6Cm?4=BVYBnCo-J z_h8gbejYHYSE5`ds%b22Dv|JfcuaAopAtoNCN#g^Byz$={_rW&z72cv>{;cy$=w~2 zL+5L#c`#)n`*d@0oLX;3^ht{Ym~1$PW%jBi@ZgEzbYOJ}8FG%ec&x9@3ZfL%+)fjQ zX6r-r`_8>^cz0gjmi;8KUM$6ep+yV{Ic-=T@Gu^A*Dn@;f*cn zCoZzPGDX|YF^$R@F)CY>?po%Z>t<&RFUTlMS$qXkQ{R1G;flru+IM`yYmgL(Ylz14 z#|HX+VmvbT{a`Axwr)O-6M&Z!>+EhKMt;fC#8*w0`vF2kSc4Cxt`3UdwtXUhg2%jg(VLYFS^?MN1gNkj z(q%rPf#=Ipknv!Qfk5JdcAGrIn+c>i&W4b5tMAobXA-!U5;4GDf2=uPjSKxsJ!JEK z{b(9a-G`fyHg9~DDcu!tJc|D3l(BkAS-Ezn6ravbX}ypZcot(as2y;fi}uWDkty~e zJl8~m*2I@KvM1Otpfb;XOH=;Mf%Sm@TQ>GGs{6(9E1KSGjmwq9b>N};$8BYosC{)f zvHg=w%`&up8!swV(n~;KMMc{lx;Hr|8us&yWS&6rW>c>pETf?FT8E!LJaRo#QT){} zV=9rgF^|5mRGDdV4{Tgmt!h}4$f!O}MkT5NKapU9HNvXn1I@+o;4GHekamrQiYW1c zz-jjUbi)3pK;@66WcL-Jk6;fJuU)x|tDqpMg@(eN|CH`yRQDptCEwTm^XA*(^6Gk@ zBVrZ4DG--S&K4&IQw0i)@GTGOerhcHyn}81E(JTydzsxb9UV<>eN~P=aSl!q$c<{r zClJ*g9&O#Xo>ce#aQltGzSVAy+jDC4!1*2cY{a`6zrfR|(NpE5i+P>e<2+Y?>q`LV zP&PyufA)7o4;>ArTBC@Zg?}m~@!&S>L_iBzt6p_z<3KB!9!e&T*NqsP?eKnsOL>32 zS^O;nOeE*VKynPO&KEd4>azz=;yn*+wKKz@M>^1&9LF(wcHA`?f9Jt7c&g-^d*3Xk zJ^kpTw+B|ojzy}P21Ur#iwWMGa%`EHe>*5em^nPD@oX;rzE$zOf&WKYK=p(a-7H5< z4Fv^rP@B8D*BP?4$;fkFlS@GV)(*S4rlyXMvcP49 z!$IQD?g@7<0^ye6#(r4B;Vox<)#i4`qJ*QaD+u3t_qRX|gjtS~>KKf#ipD zRUYRE{J9kn#vsq*%WntEjBM`hekmqN^h!p48o(L{)60MF`ROZ8`7eQP>#vu<`t#`% z-j~pynU=V~@g;EhZrgtNjN4((;nto{(e(s(tf`Ph-F-*ov~m(0{wtTwR^5=<_mk$C zAvJfDiIh8Mi#`@Z3~;(NQ=DkIa|t{u+saCM;Ejqn6zE`qZb4RlsLV-%r>3J#jqL$@ ziRLp_BPp>V{)P5IhB>N4WWIZF!vSeTAXpyw<=QcRP-x~DjPiI+D*bTtk&vgn1P~dJ zvlZwE)Z}X=`bw9XbGpM7GdlBX<=8>PkLMflYwFg>lNIO}P@f2bNIqruMU-di7kW66 zhQh9WuyXq@2!EXTG8b3$$adF5NFH5t+E?KjX^v@r22_ z%09OoT{ds}@<`cLc0Ct%Haea(8|AP0Z&TgSgS@cTD! zG+zR&sVbL1xd(EkE*I8_h~hxYt}U8$kWl*hEb!T;^Q^s%xt;}BZ+kAo{gp3JbWTHE zrHS%wRaTx=oO8>}Id;n#0>2wvBQ9`Y{;jz~YgRUD)K!DgR8r5T=B#v; zr*@w*d7nhpaQ|Sx8>MdNL!6-WQ|Nm@XqQYL7|G%EzLt9}Q{2PC>f5)|2QA=~*BBS? z1u1jlFN2B;N&t2k93vjg@9U`s`NrKC`XgP0`y@n;R=HaE33dDaP>OAej}LnZYqT&# z^LOX<%ZI_VP|%CiIbP(gH7`@#pCc3_F%j?+9I(20C_6&lIj6B1R#v|`%J&lHqpW`+ z{}Q_K3RLgk9nNpS7-fE#XYCSC-Pf1L0SyW)ED)@SQ8{$^n1eZ9-^*5r^ms`o?OpM6 z_!5u+_Nc++=Y*_v&M`OQ5-<}&Zqz^y+Xb%B{qfFD=Y`sGO!GQuJ>W_92H3VSU1i8T zx1l)NJ@Qwf39fdYxj=#`1vkC zf6kDcMg4|s=}7H{ku7dvG(@iZ#<*p$|C!u}C!KKe8-|`UHbIKoyS#E2 zjBlBO@8c@o2$nWgg2(Zm$=Z6@&8~CQEX>n9HQ+;T70IRner4cpZ^Emz&eCc?a81>G zXat;Ef`uxu28%+?gJ$T_Zhmlf2@EFnQG<<#F5iSM)HY-f7DabSA%`QsG-!UhRyNXh zESN&W13{G%#AnmUg(Y(jU3P)+3ZXm#6J;PI$2*kV-WUrO9KLZBcb?KZ-kO+zU#MwI0AeYT7o7PD1yBi*|1AsmIP&0 zXlQbie0Nc1#1nftU@vsvG=IB@VaL3s0*ZRv%{moJR@Qrv-F3{HV*-180j94Kd`l0F zvqZaRoxE7+^zrs5f}agr?UP1&bO!2emv&@RPEBNodpeC>7*8gO3#_O0$Rwv4%fCHlQyS^Yw+A9&44-Q0Vi=cb1H%+hd(1wbLg! z(q}p*@4cdH*vFE((3?Rz%sqQ=MFR%CijP|f)QJ<0^HLT&B766uttx*w=AS;dnqD{Y zTDKH%H=F3;&O6tPdL?W*!s>le9h+!2qJ5n@yj%aYrGN)E^WcF_xDtXp88;>L{k>i% zJMH?Wgf+>Mn=jo7w}wd5usPng-*bLl$cK;wbGaSnF~lh3tW8mtjf~e1qsd-vn1Ire zVgIl-3|SaNucxEiD&Ol_c4Y51pPL_*RyVyhJd3n$24r58sm^UGGx2PwCVaj=r>cb! zS(P|^YLM$r?{O+O-fOWF`Kg;LdFeaO_}f5*^$&QaTOYR59KRU=I4Ui2TKHu z#&4M%V#I`;+e0_jt{gd}sw1Uir{~qqTdtlqhR|VZ;J9>7Wn-}Z$(}ii?U2SMEtUrF zb(ro&Tii?lF=~)%i6pomNo@5vZPmFm>HNK-d_V2WO#kE zCtJ;72k~l4N`_JqF+Wrn7cIh}JK`r$Ie6AD6T~VC?*D-6LUpE4tvb(Jgjg^&rFB)v zNrRnsLRZ0vJpf>0sr?oW33c0L3D4N)9(EcHye@v)yx7=TrFFXLY7gyvuXoCi5S3im z?yL5&UQQ;Mh}KkfqPZBve& zW=&muY9EIM`?dpK*(4~O`7HlgnW16=wt<|?fKgnzL-{>;&qJ9ql^D-Z)N$CXxl6zBCtK8MM|xvX_^Xu<7lUK1Nkv?Rea=BqJPsT`1UdSl%HvE15(96wLb5ooW?)D$_x*C!~>-;c> zM)Rne@vVLJ6@DicT2&G|`cGb1<9m0R)gE#^7|n4Ra(42l=?a6c1i-I87Z7g&Y(aaxrM1<6_!^ zmU&UQqc~@?d(%2rsXEQF;XU)Bk{ss9fmLY8q<96AJ2ZkE=fL3fK)kZ53E_u0vJcA1 zT_ranOhgo}y7G|gS4Pdn2SCpH-5mzRVXWu5A7IUid6D`9QyjIE*1J8oTZ8B4gz`G_r&bh$F-Vf84yU zbi{)?=YVc#Ju)Y_k8+#sW=Of)Fj77){e4ZWYbAC=Ft{M~wo3l5yozuCCdPKidn7(} zdDKpOg;D3?X40%Pb2p>vhCDmgG$~{^p=^7;!$_k|F<>_9p5Sxmgl`49`!?wCzNB6= zILct&$$OEpYI`Z4((0mWi3Ats-VYuyU^9trw`6F2z3*A4@p(CPzha_i#@Gxf2fLRq8505JJ5J!Afm zI=qBM6+bYTNmwF7h^l@>_~f{UTRBu zX25(<#Edm(7ZXh9o87tT9n3M5Kb&X&_^jg}-*{oly0KiZ z#gD>zY}!v&_)beWh7p&>R~(%_%zx?q12-?a{D!Zry$$`cv1N)$b$fz`;?NsYRjZTw zaX~ej#jSbfL|WF3p4R;y47bucckF0S@vjAilp9(Ua(!rPUU0%GXezc^m&IQ>@rm3I za1v5W5ZjzJ34GgI@9mc5`E<* zO&d-uS$8i160Q`ianwuli=AHMuovgl-0qShOO^OS>Ma|tD^-M*g0Th}#$OaA zWjg2JULPlV&!|g0iuj^3@+fLWlF)x2I8puu9sktZUs{ZYw+Y|8>`2|f$-TZDdEQQ z>ep-D$$?);hnQ{#MJwSWC0f(5VQCt5FS$G2;v!W zk7I4gECA(l4W+O&sXErtOwjN-mhPPj>zg&IySv@lZUp2f8G2%WT(Rp?D$;Ebrf zdK~opaOD*FQL5sCI7@v~ojCO9_BcssM*mysi!20>7h8@0;rbD5ZG>b5G5zAFzjO== z+7`?6%4)3qg}_8>@WE@9Ew#4ldO3-Z1E#uGi|;fgx^u|LbA<0~WGN{hr@+F`b;Kce4F4toq zTLuKXI85k`M!ud}%?#5Z`vJo87!_CK6MsbxZRlM5QW|qK)q*>jp0YYzjsv=ee3*kq z@I3v3X{YJ!sa)H{YM&s{p&wR7-r*Mb!8Y|@Ie~wp$XK%TBx2Q3Dmg5bdjcnR2e~lO z9kVBF6$R4o3CZ>Dui?x_Z6F|v6r%Gy@olL2&L zAU`!^^r1ur7RvuHNMUOCt}pN0{n=-a#y7yCtP@}|h(CWPbfaNUlrTGMZ&Qvw`u2Zz z+kgKK`bCEGjCT9z6P40pcS@T+Uj1hJjLpXtIxT|4kEa7zk2_B}>vK-~DPZifZN+Dw zkkdW-bp1vsw{4HsDGaa1ntF##ZDjA_6xEaSmaj;{pw*~-|Gxx`|2#9gmw`_Ci^tpY zRG1I1SG>RBB*5#qgehH_mW$7Q3E-JDlBQ5P=kJTjz9_)red$Zs-7SbnRS2>|hi&KL zOC|z_3mXj$iQgUyL-ant`GQSvCB6m?=G#k1{_IY_#FGoTOJL(A+Pw?-N7xqsg3FN= z^du8#a+wL@6T~m_OMDRs8+<5Qvqd;zOypQIGW{(q;Mv}~9L*d1#<21BYK{Zslj8x* z!s77fNzTQ3@E`IypwRIMWG31LTqw~Gth#06uk-%*j_rT$0*F~+1uP0lIKu#13Gu6=cQpV(IRixnW9@J;t8>}H|H2QvB*^)q|U1i zKDblLS|*HzCAo1OD{_QyGQRNf=k6AH=U%5Kse{W4&7AoyACzI0=gc=979Na?#AVe@ z8HCS0qpdVc=@eGVY5>n?<&A_gHsc_x$ zOo9pgA_olovc})b77vuIm5a0IfBeYdX3(VfyjaT2X)s2o$E$?Wj&!9p%ifVRkH>Y- zOL&Vnth%56Fg8|+Oks%4A}v{Juk*J%GG1~!`po{>xId8>Z3g50Ei{*FHV)pBy-d+^ z3`E#B(V5VYCu3s`TvE(!cRe@9m@9RpuO*3$vd0|muYaANS=yglF_%57mt7qy5mbIc z66DyQY@3zf^`-Ta%+=w``vMoFbQQs-&J&IH_rnX~g8XGWg&*WHC)ZNWPg3@kaxzAB z7M(R^Y>kaqM&_LGr!gmbm@l%sI~~`f8Jo97B3uOSd|T4Zs^2Ls$=39`eQ>N$_n3&h z;#FSI<$=AeVQg=8^^+%0deqDEXteAsOGhM_U8KW0r+n}xwYv>ccc^rTJ#Dk2$#y=U zdnfQbrI1CXE2&m5Gh4f+D%UM^z0T~{`cAuZR`W=B{;8wu9_F!_SW_*|<^Cv}<79*5 z7yZxO?cXTQQqJ}kN0%|I`X(s?X&6SvwaLG?6_{6VdYK++hhut%T=Oh^P#dg_6f$pq zeSX%i&0~E%CvhxTCViw*#Vv?lMffbsH=ldxbZuWTjMz}R(u@{AzRC22_7p)GR#z!L#!F1)8MQbI zD+%F&YBV1E#vfXw9xr-&hX#t1x4uJCP4~JsTEWUQ%eR>vWDz^e-U;Mk)0EJUGQX7ZVj;#Yuhw>CcaK7>z=q{MQr6L(m$41 zsb6YGKY!zhvcgA5xg^am&M7&^u$?Ub<|}G?=HO2WL={cRQWqE2D|I3vZY%e?PnS<9 zFW`nx@%$7tu-=e*TaS=S?x_hfyj8mE6T@$ZZYIPVrl4_GzIh$WQ zeef`xlaF+XM*)t6q3H|A5r81U74 zvN;#xa{=N_SloyBr5ItFcq3-{a_ms24aR8l17+$$xV^_fEi*|C7E`f4Q*w1G zDr!Rh2Vp8pHoatWgAQZEZka#V7;i@r_S1kVQr&KoRR#|$HhY+L`Tmqmxo1*?g_Vc{ zt!r@lvwJsbT_5yn@CfdTbgpuLP2s~*Q8#2py6-EI5}%|_QQWb9tC29|GqbYI)Ni!Q zs6u6W^GoEuU}KjAp19_8NPb(almka#f?#9RJ>N?AWF6($v{I%GO+iG7y~mqoH{@A3QtR3(zaPao-)Zy1YbOwO%WLhnBhd!D}eh27DRtLw`Ke%yyI zypDzwc%n6)u8NVWI@y%{ObSx%YPz?{<_vF!*){1n6~=5!q5F2+);ccy5&+why6NLj z+K}kFt{}3&u4wl8I|T~zee_oFb3sp9wUJq}rq-|f_Di*d$@o8SOL-M+Qz90eMJMJe z>JXemizU&VF_OM5^QQ;)T%~Lci9Jto?CH>N@x2sGYqoN{iLjva>o+4}-MD4IJ~jS5 zQOia*`9?j?UP_(2d1rn2Iz(^3v9b0Dg6NmUBDVeBCjPSHO9*)rdVE%RY>%JSrqt^P zTm3h^+HQ*xp_ZBQyse+b3g0#EZ{Sw`+}-eOB`wK=dDf)egKv<{ubP*?#LB)~>s1$y zc>}rlG6z#b*dld%kGKziRc6YiaqZH5WV}hR4ONxx;2l;&Un&FrPdj>2FX#rBdiaT5 zu==mdFjK9Iy-UZP60+Y}Pqv&b@4zYa`b?;Twf5d8WQnbx({tNbI_~E@W(svj)i(aqr%H1wJD9#)WtUqXbUYfn+I z^?AyyFm*QYYTj$kDi^Pq@MTCl{={jNvvr+&eWcq`dh6?Fvi}2yKzYBO`MA<2L}r@0 zZH&>z^S|A4TqEuOURT71=tSG-HZ?>mv6 zwc_o9H^B9~nVMn`9Llq))lyk3SGutgdy#Xx1CKskkKHut#m$y_!q zWSoL0#XT5T43mh3d+@FlPhLZ|k81aZVb{v@N{o_G3iju(Pn;F(V?U5wE?+sL*p zX-aCFm1|OVqg|x3BwZ&eOiz97+%S0u7=pftJa9YFO~q1lJj!N8tV$O?U9IH;xmixm zBbCI`xsq?9TNavx-T|?HJ*qI2U@$mahc9oGaO(4oLlj^+qX6rvj7)YZtUppqvlN;w zsZsu=kji*u30KRVa8j!!m7j2_OgolvQtpJSw;Jc=C)_@niX}T3tfw;j_UR~~p31b# zIKm8R7KP9tj{~2TCjQ2S@*SbYsii+5Yv$SJD~)!q!HZ(ePBkJt5?b^1R2IgHDPKFN z)Hn{JIn)?RsaMhzvNp!Utp4CU8{<9&OpSH4sS-7 z0wz%*oIQ-ZROYbQ^@TiEtYu8IIbMJyErvSPxaYc$haH)16F84hOaI2x&A? zNTR$u8W$ujG2tfPLA8l7h6I&rj+kdp@`CQ!o>9hhN=U*CG=gLj8bsD72MzKbA@@Me z^yyR?Qx#?|K0gfK;2M8Ad{<*YGbZY8Ti*7(9fX{?kvjJ=8>nWCOfV44Z zz)$tDZ8Ay~yj(2Q<>>^OSS53~&(ohsXD0fajqsYm-;7l=?jc2LqL>R+UY_c$ zCn5l*27W?i0s94!;^e7ALRc=M(>oSsSWuo)ql6Wa&rg@&GN_2>4Wf|41CTeBhE#32 z54^%KwR%-@AI_Y;N~~NsPf6#0C+hVp4BWw&tXts~6XpBm%s^8-7-ekP1~SjYD)WEh zM~VLN@#3n{sNGzAJg@w9@pR-grMR04Y;o>0bEd!idW5h>G;trv7_@BRFLmu!m#Jj* z$>x%Mv4+xO)tlzS>%;4euf?Me1N(Ji=*mO@S^R>xOTn}i3khGPOCENV)^~7z^x>Os zd>25y7O>GESoSMkiV9XMc%YxJhmNJrKmOw%|K3(r0q;*(YRum9uQ@PMWj;Sw0J-~TF|;{U>0JH1s#7VswT zg19(x_xPaZbjGuHNO;$~#>&$itHgqVo)ysSO{O8KKS%y`YSGb#6Q|A4Yvb0cW5Ii0 zHKxpm0jN%mH{QmTHX&>J_8&90&)WZWxV@pjga9HeySUN83X`K4Kg-yFb2KKbiqhPg zl@pKo?<=+A{K-BGqp>5zuCYE;KCJro=&#uFDF(6Y42ZRD3+u|q=1*%;J6p?i8M#nO zi9Oz}zY5h#wIUWY-x(0AN@vycv+70pFe71C!gGiUHo zWlHwRx_wLZwpZF%pPr8CX<77Z$A?6L8M=jkf+vjl6(V-GFhQmOUWNbqzu4jvk5=?@ zNg`Mk79ii|!LoDSVhIS%A19P$5*WvJsY zu1D+f5rada5XUolVIkuz<`q1MeI7iATz%SzVH?hgVgv|tpx|jY8wQ<$G|~prNbi?4 zwGE`Hyn|YMWRQg?rmr=q5_lS`u!Rks%w)RIO{^4HZ-hL6EfxIUs;zW=vu2 zNidfto?|m9Y)lwQ_=GG914?tE2?OJT6F&*l<{B*!gyW4BWF`e65fB*2t)khlDO*dz z7iDbd;OrH?VaCv365~OJA_>^ggkg2sCNEjU~Tds}g z;T7I=j4ORU$MU6b$>Z-VOeqme$%To9OIuYjG|M*O-@u4#iD1?knAgZ08_EJNQWjo4 z5xhDBk0RJ*g`e0+1g#N)*2AB$@S2I>H6!pMCHMjIl*;rSx6&uzIBt@9veXs-xi2mXa~LSdT!zR?P9b4w7-c*kC%i z)t_!bJ7>Wq<9o5mfa_?JtK}HeBL>T4+%KL7u^h<86AExuGUgXA7Oi)ETCjeFNr=NeZj&>#_E!{cn11+q$z(#lyoda9`>%+ zRt5{yVY4Mb1Bz{phpFjyEv?w`6M>L~D_MJ%Q=?E5h`1L3htxpo9wh{b-v)!k56NI) z@~=<~6H`aFCyG8K%sGVEOBNRYvIJO?1M3NC*fvrl(D)Z6K&uHO*}}H{a;S!cP@R7v z0-(AF$PDe`b^a9yfEoh01fGx)1;nqR0idSfh-|#q4F6`!s$#K(s`IaBfOZb%zN3*1 zY4$=8IsRo0;4Xv-*;w%O*-tDO{;dkYw4Cs{p}CxobZqYo-Lqi^B>(0_c(p2^B{GL| z9Yz|2l#uI13dzkpWFaNvd8|PYNCukAiUzf9=lV066Nd={G#Tq-C1R>7iv=cF++Z8Y z_#Z0`9<-PNH; zoLP*rS2G#MOBW8Tc>Zc8<9n$kn4k~i8K64H2t6qQc*&T*Rt>=$+Xm!P4J{+6zBiMt zgPItEivs3#GudjWNrJghv>{oiF)N~(P&cYkaCBZ`;GHL17PUHoN7loHcwj@0`ef^& z*6_itUufsa7D%lLf%#O+TqDxYlP#9oIY1rj23iX=*{Z2s0MPZ@**ULy1lq9U;xQAq zm)4k9wH4+OH)H@2V;&`Hzsfv1=QodPSj%?_M9m{i{9(j6;(|!$uttod!Q-EYS6{9# z+mF}XyUVN3?Ov~QJN(q?bspNcEMR`&QX`g9JmA5g&jRI_F*RZ^1%ZBf9CTP{{351C ztR@#)=P#Z9<<;$7pM`e8FJNlKbP7PbySZUu@k^H)v7UmkdMqe@c~T>$QwY@X`thOL z`@}-zmnAh~Jq01&cLM0~%a9tepn) z7GAQ2A)ZI!_3t|!cCuT^R)**=joZ%k<;UA@?>81&vZW!m0?>kU+)B1Mq-qdWV0x3S z4oM7vx_`V4NX=HlZ9)n_>vsk$tYnNV`77hg-Q(bMM52q;WSlJ3LjZ$wAXbxcu+#{H z>jj`C<6o&6ffik3Vl^2DOXp#rLFq15lku?RuZaHLWAA!+bKCBJzP!I}|0Mu5AvgOg zqkrG+_3)T6_uLGIA%Z4j>{@jt)HuQ>nA?pQc1dO4Nbjyn?^Ap4?LRX`mk{Nil zP6SynY3PX?jW&}_fTanvrU-r$>>0`CQjo`EWD=Y_jHkLG$iZ~40vvFhP@5rP-$=8Yoph=9DYfuKJO0i4u}U#J}WyA1(^h7 zEgobIco`xH^MMtOC(LQsF%gq*ZS<>IX;XY6FxZW^dIQv?B}1I_zeMbm1YTJC%{Pf`HT}&IL1-N z!ZYmOx<*cQY;#DU9YbJ(@H2h%6DPd?#ra)$ilA`I#CRTB5cOjYErI^QJ3DdUXdF=o zY77dZL|xO$R*?oA*3#LH^ot}f!clDw`4hK<}l zgtUxGiaud>5`K-3mOZXks$Pd7Z^5N_(!1{5I5?!8GlBsO6tMWGUxm&P_jCR5b=bS> zJUrYzq@^DztBYfXpF6j=nS|!5(B9+FCYnnpV9^|Bf@C19 z!fQg614g)jbeNx0`$%Y-P+n#k$r{Q>-4CY`<%6Q1Rg{mh^Dk2+2- zPqcCN?q&-GssgP=fN2r#R3o9Xu7eBfLCaJ)-OEtM)ItU?O>PyFx_und3-SohI`!9i zu+mcKfi8Gb(kMNN3*!+6Q!iX zTsFS*PDx*%!=OnMW;#}NnszBiQvH%hh=j3yc&?nwX5Mx;%!rTsu~} z`XxW3zpFjy|2h`mY|wdd1SG!ucFFudB$c&+{<^w`T}BMW#% zMp|K#qo-?J>noicNB}+AT2F*_l4aY~f|E;K+ToBb;;1(8jN$)TFFNHMlU5^exuxy! z6C7m(KoVG32w^BN!cVbIF!Wp zs+k?zk_f)AHAUu|4D9y{u_gWC7;6hzi#c$^wu#FV@C8?y0gI-a8$_UoO{?0Bi@x z0MHL%A4+zi#FRqVhCuSWJ0^RjpY~@7Kf6!1Ae$7pP#W7}OyVKTs~cTVn3Qsd7?w`V z?*ZXB=NuIF%@I}@q2w!LN&8suxrB-^f%_o2?`VxA3ilezkMxAYV?iPLU{(q#7iwjy z7;^}-F)ItK)__|P4h5aGiXn9@7v8m#DYB!kN$Sgv`ZlJ!=yFornZ(Y%p+TvI2By zBkqRDP~cvhmS?EX)D&1x`ii?n2Jp9RU&P%g;=TL8cL|64eS&*Cye5&WLU=Dp@H#JhA;f0vHD{FDg=LQO&xM{B_IA>_v z?R{S*0z%g4w!hn?&ogvjkIpRnd-`m3(XU1`m`ai+SxD~!q_aO!r08LFfvOGlBnPW z&cJ5>Lf1glg&DAD&9RxQ=GeXN^akCVZYM`_XVf1<yXtKCJ9p#`L)*qqYU!$aMF`tR)5`I zbb=cY;-CA!Sxq*#&bA~?kN30}L)V`6RimX*PhwT7@10J|Cji5{Xd@g8m1H;>6a2QP z-A+|cspPt+bWlE{$|oViUiwp$>Yj8n#kyy3 z2R-PXD|JsQV1l}5GWFx2dj@pR(EWjh=$`D7i!a17(d`krwpf*yu2wmo!_5^&fqLbA zB-Ia3ZQ)4_WRP6i<28JV_fCu?V(T3;*?heEq(jWreL4DM60l|06~#5GqL@%q6q758 zVrD&2ETx(#X4Vqj)#ho24r4nh-u@}k_7p`iA3gED{m}0`bovk|aJ+)P}C<9X} z&cNJe05?>=i)fhCVP`rXO(^F?3Uk{F!3`$nDjBynSUj%E;U={!SOt-}LnKfHm8fDo z8hMhS^5oE>Mk3$vxQit6THwhIN?1>bu%CezP(tY#y^hfWDsIHf0uob0Dxb^GctsLr zR}U2lA(_oGHJ|*7B)WiSKIizD$nEfDJgf*K=~V83NTlIHwq{!}DM_Ig=-+qCD;l06 zXhlMzD#EdekY6^E({&WCTl7;3hD~g-dW2hTKHlTEuHToAn+)Z6{|+XI2AA{WQq^o5G!_pn1a&1MeKr#~z+E z>@1TnvN`doE*3JaA>sIn^=ciIvRsw2{IlS~`}DRQ>t4r>?n5|d%X*KaoCXyj=Uxe+ z?R*9)+s?H~SbZQGESBJ2mA?d9wYwcWqfohAzf=ZC#oRDg!yOa^E2sl<3BMfN`0k^4 z*WfA@-uZLHIIsYn#uPJ!jh)Mw>-kc^X?SqL*YaJ~PQ)tiC^=M-!EQxmNOJROV!*Y? zu<6B)1=#{*dbm!noD(J1D8NP!U8%3iTvi2B`MVRw<;eoES-YqU@$lpS94+zI$aB

2M$3xq~X5$61z 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 hvE^kB_#5zLYD*+DiLWcDG + * + * 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, 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. + */ + +#ifndef _TUX_DRIVER_H_ +#define _TUX_DRIVER_H_ + +#include + +/** + * Id enumeration of high level status. + */ +typedef enum { + SW_ID_FLIPPERS_POSITION = 0, + SW_ID_FLIPPERS_REMAINING_MVM, + SW_ID_SPINNING_DIRECTION, + SW_ID_SPINNING_REMAINING_MVM, + SW_ID_LEFT_WING_BUTTON, + SW_ID_RIGHT_WING_BUTTON, + SW_ID_HEAD_BUTTON, + SW_ID_REMOTE_BUTTON, + SW_ID_MOUTH_POSITION, + SW_ID_MOUTH_REMAINING_MVM, + SW_ID_EYES_POSITION, + SW_ID_EYES_REMAINING_MVM, + SW_ID_DESCRIPTOR_COMPLETE, + SW_ID_RF_STATE, + SW_ID_DONGLE_PLUG, + SW_ID_CHARGER_STATE, + SW_ID_BATTERY_LEVEL, + SW_ID_BATTERY_STATE, + SW_ID_LIGHT_LEVEL, + SW_ID_LEFT_LED_STATE, + SW_ID_RIGHT_LED_STATE, + SW_ID_CONNECTION_QUALITY, + SW_ID_AUDIO_FLASH_PLAY, + SW_ID_AUDIO_GENERAL_PLAY, + SW_ID_FLASH_PROG_CURR_TRACK, + SW_ID_FLASH_PROG_LAST_TRACK_SIZE, + SW_ID_TUXCORE_SYMBOLIC_VERSION, + SW_ID_TUXAUDIO_SYMBOLIC_VERSION, + SW_ID_FUXUSB_SYMBOLIC_VERSION, + SW_ID_FUXRF_SYMBOLIC_VERSION, + SW_ID_TUXRF_SYMBOLIC_VERSION, + SW_ID_DRIVER_SYMBOLIC_VERSION, + SW_ID_SOUND_REFLASH_BEGIN, + SW_ID_SOUND_REFLASH_END, + SW_ID_SOUND_REFLASH_CURRENT_TRACK, + SW_ID_EYES_MOTOR_ON, + SW_ID_MOUTH_MOTOR_ON, + SW_ID_FLIPPERS_MOTOR_ON, + SW_ID_SPIN_LEFT_MOTOR_ON, + SW_ID_SPIN_RIGHT_MOTOR_ON, + SW_ID_FLASH_SOUND_COUNT, +} SW_ID_DRIVER; + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Error codes + */ +#define TUX_ERROR_BEGIN 256 +typedef int TuxDrvError; +typedef enum +{ + E_TUXDRV_NOERROR = 0, + E_TUXDRV_PARSERISDISABLED = TUX_ERROR_BEGIN, + E_TUXDRV_INVALIDCOMMAND, + E_TUXDRV_STACKOVERFLOW, + E_TUXDRV_FILEERROR, + E_TUXDRV_BADWAVFILE, + E_TUXDRV_INVALIDIDENTIFIER, + E_TUXDRV_INVALIDNAME, + E_TUXDRV_INVALIDPARAMETER, + E_TUXDRV_BUSY, + E_TUXDRV_WAVSIZEEXCEDED, +} tux_drv_error_t; + +/** + * CPU number enumeration. + */ +typedef enum { + TUXCORE_CPU_NUM = 0, + TUXAUDIO_CPU_NUM = 1, + TUXRF_CPU_NUM = 2, + FUXRF_CPU_NUM = 3, + FUXUSB_CPU_NUM = 4, + INVALID_CPU_NUM = -1, +} CPU_IDENTIFIERS; + +/** + * Descriptor structure of a firmaware. + */ +typedef struct { + CPU_IDENTIFIERS cpu_id; + unsigned int version_major; + unsigned int version_minor; + unsigned int version_update; + unsigned int revision; + bool release; + bool local_modification; + bool mixed_revisions; + unsigned int author; + unsigned int variation; + char version_string[256]; +} firmware_descriptor_t; + +/** + * Descriptor structure of sound flash. + */ +typedef struct { + unsigned int number_of_sounds; + unsigned int flash_usage; + unsigned int available_record_time; +} sound_flash_descriptor_t; + +/** + * Descriptor structure of ID connection. + */ +typedef struct { + unsigned int number; +} id_descriptor_t; + +/** + * Global descriptor structure of tuxdroid. + */ +typedef struct { + struct firmwares_t { + firmware_descriptor_t *package; + firmware_descriptor_t *tuxcore; + firmware_descriptor_t *tuxaudio; + firmware_descriptor_t *tuxrf; + firmware_descriptor_t *fuxrf; + firmware_descriptor_t *fuxusb; + } firmwares; + struct driver_version_t { + unsigned int version_major; + unsigned int version_minor; + unsigned int version_update; + unsigned int version_build; + char version_state[100]; + char version_string[100]; + } driver; + sound_flash_descriptor_t *sound_flash; + id_descriptor_t *id; +} tux_descriptor_t; + +/** + * Simple callback definition. + */ +typedef void(*drv_simple_callback_t)(void); + +/** + * Status callback definition. + */ +typedef void(*drv_status_callback_t)(char *status); + +/** + * Tokens structure + */ +typedef char drv_token_str_t[1024]; +typedef drv_token_str_t drv_tokens_t[256]; + +/** + * Logging target + */ +typedef enum log_target +{ + LOG_TARGET_TUX, + LOG_TARGET_SHELL +} log_target_t; + +/** + * Logging levels, in increasing priorities + */ +typedef enum log_level +{ + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR, + LOG_LEVEL_NONE +} log_level_t; + +extern void TuxDrv_Start(void); +extern void TuxDrv_Stop(void); + +/** + * 31/08/2012 - Joël Maatteotti + */ +extern bool TuxDrv_Eyes_cmd_off(void); +extern bool TuxDrv_Mouth_cmd_off(void); +extern bool TuxDrv_Spinning_cmd_off(void); +extern bool TuxDrv_Flippers_cmd_off(void); +extern char *TuxDrv_SoundFlash_dump_descriptor(char *p); +extern void TuxDrv_LightLevel_update(void); +/** ------------------------- */ + + +extern const char *TuxDrv_StrError(TuxDrvError error_code); +extern void TuxDrv_GetDescriptor(tux_descriptor_t *tux_desc); +extern void TuxDrv_SetStatusCallback(drv_status_callback_t funct); +extern void TuxDrv_SetEndCycleCallback(drv_simple_callback_t funct); +extern void TuxDrv_SetDongleConnectedCallback(drv_simple_callback_t funct); +extern void TuxDrv_SetDongleDisconnectedCallback(drv_simple_callback_t funct); +extern TuxDrvError TuxDrv_PerformCommand(double delay, char *cmd_str); +extern void TuxDrv_ClearCommandStack(void); +extern TuxDrvError TuxDrv_PerformMacroFile(char *file_path); +extern TuxDrvError TuxDrv_PerformMacroText(char *macro); +extern TuxDrvError TuxDrv_SoundReflash(char *tracks); +extern void TuxDrv_SetLogLevel(log_level_t level); +extern void TuxDrv_SetLogTarget(log_target_t target); +extern TuxDrvError TuxDrv_GetStatusName(int id, char* name); +extern TuxDrvError TuxDrv_GetStatusId(char* name, int *id); +extern TuxDrvError TuxDrv_GetStatusState(int id, char *state); +extern TuxDrvError TuxDrv_GetStatusValue(int id, char *value); +extern void TuxDrv_GetAllStatusState(char *state); +extern int TuxDrv_TokenizeStatus(char *status, drv_tokens_t *tokens); +extern void TuxDrv_ResetPositions(void); +extern void TuxDrv_ResetDongle(void); +extern double get_time(void); + +#if defined(__cplusplus) +} +#endif + +#endif /* _TUX_DRIVER_H_ */ diff --git a/include/tux_driver.pas b/include/tux_driver.pas new file mode 100644 index 0000000..e1a471a --- /dev/null +++ b/include/tux_driver.pas @@ -0,0 +1,233 @@ +{* + * Tux Droid - Driver + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + *} + +unit tux_driver; + +interface + +uses + Windows, classes; + +const + + DLL_NAME = 'libtuxdriver.dll'; + + {** + * Error codes enumeration. + *} + E_TUXDRV_BEGIN = 256; + E_TUXDRV_NOERROR = 0; + E_TUXDRV_PARSERISDISABLED = E_TUXDRV_BEGIN; + E_TUXDRV_INVALIDCOMMAND = E_TUXDRV_BEGIN + 1; + E_TUXDRV_STACKOVERFLOW = E_TUXDRV_BEGIN + 2; + E_TUXDRV_FILEERROR = E_TUXDRV_BEGIN + 3; + E_TUXDRV_BADWAVFILE = E_TUXDRV_BEGIN + 4; + E_TUXDRV_INVALIDIDENTIFIER = E_TUXDRV_BEGIN + 5; + E_TUXDRV_INVALIDNAME = E_TUXDRV_BEGIN + 6; + E_TUXDRV_INVALIDPARAMETER = E_TUXDRV_BEGIN + 7; + E_TUXDRV_BUSY = E_TUXDRV_BEGIN + 8; + E_TUXDRV_WAVSIZEEXCEDED = E_TUXDRV_BEGIN + 9; + + {** + * Id enumeration of high level status. + *} + SW_ID_FLIPPERS_POSITION = 0; + SW_ID_FLIPPERS_REMAINING_MVM = 1; + SW_ID_SPINNING_DIRECTION = 2; + SW_ID_SPINNING_REMAINING_MVM = 3; + SW_ID_LEFT_WING_BUTTON = 4; + SW_ID_RIGHT_WING_BUTTON = 5; + SW_ID_HEAD_BUTTON = 6; + SW_ID_REMOTE_BUTTON = 7; + SW_ID_MOUTH_POSITION = 8; + SW_ID_MOUTH_REMAINING_MVM = 9; + SW_ID_EYES_POSITION = 10; + SW_ID_EYES_REMAINING_MVM = 11; + SW_ID_DESCRIPTOR_COMPLETE = 12; + SW_ID_RF_STATE = 13; + SW_ID_DONGLE_PLUG = 14; + SW_ID_CHARGER_STATE = 15; + SW_ID_BATTERY_LEVEL = 16; + SW_ID_BATTERY_STATE = 17; + SW_ID_LIGHT_LEVEL = 18; + SW_ID_LEFT_LED_STATE = 19; + SW_ID_RIGHT_LED_STATE = 20; + SW_ID_CONNECTION_QUALITY = 21; + SW_ID_AUDIO_FLASH_PLAY = 22; + SW_ID_AUDIO_GENERAL_PLAY = 23; + SW_ID_FLASH_PROG_CURR_TRACK = 24; + SW_ID_FLASH_PROG_LAST_TRACK_SIZE = 25; + SW_ID_TUXCORE_SYMBOLIC_VERSION = 26; + SW_ID_TUXAUDIO_SYMBOLIC_VERSION = 27; + SW_ID_FUXUSB_SYMBOLIC_VERSION = 28; + SW_ID_FUXRF_SYMBOLIC_VERSION = 29; + SW_ID_TUXRF_SYMBOLIC_VERSION = 30; + SW_ID_DRIVER_SYMBOLIC_VERSION = 31; + SW_ID_SOUND_REFLASH_BEGIN = 32; + SW_ID_SOUND_REFLASH_END = 33; + SW_ID_SOUND_REFLASH_CURRENT_TRACK = 34; + SW_ID_EYES_MOTOR_ON = 35; + SW_ID_MOUTH_MOTOR_ON = 36; + SW_ID_FLIPPERS_MOTOR_ON = 37; + SW_ID_SPIN_LEFT_MOTOR_ON = 38; + SW_ID_SPIN_RIGHT_MOTOR_ON = 39; + SW_ID_FLASH_SOUND_COUNT = 40; + +type + + {** + * Simple callback definition. + *} + drv_simple_callback_t = procedure; + + {** + * Status callback definition. + *} + drv_status_callback_t = procedure(status:PChar); cdecl; + + {** + * Logging target + *} + log_target_t = ( + LOG_TARGET_TUX = 0, + LOG_TARGET_SHELL + ); + + {** + * Logging levels, in increasing priorities + *} + log_level_t = ( + LOG_LEVEL_DEBUG = 0, + LOG_LEVEL_INFO, + LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR, + LOG_LEVEL_NONE + ); + + {** + * Descriptor structure of a firmaware. + *} + p_firmware_descriptor_t = ^firmware_descriptor_t; + firmware_descriptor_t = packed record + cpu_id : integer; + version_major : cardinal; + version_minor : cardinal; + version_update : cardinal; + revision : cardinal; + rlmmr : cardinal; + author : cardinal; + variation : cardinal; + version_string : packed array[0..255] of char; + end; + + {** + * Descriptor structure of sound flash. + *} + p_sound_flash_descriptor_t = ^sound_flash_descriptor_t; + sound_flash_descriptor_t = packed record + number_of_sounds : cardinal; + flash_usage : cardinal; + available_record_time : cardinal; + end; + + {** + * Descriptor structure of ID connection. + *} + p_id_descriptor_t = ^id_descriptor_t; + id_descriptor_t = packed record + number : cardinal; + end; + + {** + * Global descriptor structure of tuxdroid. + *} + p_tux_descriptor_t = ^tux_descriptor_t; + tux_descriptor_t = packed record + firmwares : packed record + package : p_firmware_descriptor_t; + tuxcore : p_firmware_descriptor_t; + tuxaudio : p_firmware_descriptor_t; + tuxrf : p_firmware_descriptor_t; + fuxrf : p_firmware_descriptor_t; + fuxusb : p_firmware_descriptor_t; + end; + driver : packed record + version_major : cardinal; + version_minor : cardinal; + version_update : cardinal; + version_build : cardinal; + version_state : packed array[0..99] of char; + version_string : packed array[0..99] of char; + end; + sound_flash : p_sound_flash_descriptor_t; + id : p_id_descriptor_t; + end; + +{** + * DLL static linkage. + *} +procedure TuxDrv_Start; cdecl; + external DLL_NAME name 'TuxDrv_Start'; +procedure TuxDrv_Stop; cdecl; + external DLL_NAME name 'TuxDrv_Stop'; +procedure TuxDrv_SetStatusCallback(funct:drv_status_callback_t); cdecl; + external DLL_NAME name 'TuxDrv_SetStatusCallback'; +procedure TuxDrv_SetEndCycleCallback(funct:drv_simple_callback_t); cdecl; + external DLL_NAME name 'TuxDrv_SetEndCycleCallback'; +procedure TuxDrv_SetDongleConnectedCallback(funct:drv_simple_callback_t); cdecl; + external DLL_NAME name 'TuxDrv_SetDongleConnectedCallback'; +procedure TuxDrv_SetDongleDisconnectedCallback(funct:drv_simple_callback_t); cdecl; + external DLL_NAME name 'TuxDrv_SetDongleDisconnectedCallback'; +function TuxDrv_PerformCommand(delay:real; cmd_str:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_PerformCommand'; +function TuxDrv_PerformMacroFile(file_path:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_PerformMacroFile'; +function TuxDrv_PerformMacroText(macro:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_PerformMacroText'; +function TuxDrv_GetStatusName(id:integer; name:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_GetStatusName'; +function TuxDrv_GetStatusValue(id:integer; value:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_GetStatusValue'; +function TuxDrv_GetStatusId(name:PChar; var id:integer):integer; cdecl; + external DLL_NAME name 'TuxDrv_GetStatusId'; +procedure TuxDrv_ClearCommandStack; cdecl; + external DLL_NAME name 'TuxDrv_ClearCommandStack'; +function TuxDrv_GetStatusState(id:integer; state:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_GetStatusState'; +procedure TuxDrv_GetAllStatusState(state:PChar); cdecl; + external DLL_NAME name 'TuxDrv_GetAllStatusState'; +function TuxDrv_SoundReflash(tracks:PChar):integer; cdecl; + external DLL_NAME name 'TuxDrv_SoundReflash'; +procedure TuxDrv_ResetPositions; cdecl; + external DLL_NAME name 'TuxDrv_ResetPositions'; +procedure TuxDrv_ResetDongle; cdecl; + external DLL_NAME name 'TuxDrv_ResetDongle'; +procedure TuxDrv_SetLogLevel(level:integer); cdecl; + external DLL_NAME name 'TuxDrv_SetLogLevel'; +procedure TuxDrv_SetLogTarget(target:integer); cdecl; + external DLL_NAME name 'TuxDrv_SetLogTarget'; +procedure TuxDrv_GetDescriptor(tux_desc:p_tux_descriptor_t); cdecl; + external DLL_NAME name 'TuxDrv_GetDescriptor'; +function TuxDrv_StrError(error_code:integer):PChar; cdecl; + external DLL_NAME name 'TuxDrv_StrError'; + +implementation + +end. diff --git a/include/tux_driver.py b/include/tux_driver.py new file mode 100644 index 0000000..717f5c6 --- /dev/null +++ b/include/tux_driver.py @@ -0,0 +1,404 @@ +# Tux Droid - Driver +# Copyright (C) 2008 C2ME Sa +# +# 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, 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. + +from ctypes import * +import os + +E_TUXDRV_BEGIN = 256 +E_TUXDRV_NOERROR = 0 +E_TUXDRV_PARSERISDISABLED = E_TUXDRV_BEGIN +E_TUXDRV_INVALIDCOMMAND = E_TUXDRV_BEGIN + 1 +E_TUXDRV_STACKOVERFLOW = E_TUXDRV_BEGIN + 2 +E_TUXDRV_FILEERROR = E_TUXDRV_BEGIN + 3 +E_TUXDRV_BADWAVFILE = E_TUXDRV_BEGIN + 4 +E_TUXDRV_INVALIDIDENTIFIER = E_TUXDRV_BEGIN + 5 +E_TUXDRV_INVALIDNAME = E_TUXDRV_BEGIN + 6 +E_TUXDRV_INVALIDPARAMETER = E_TUXDRV_BEGIN + 7 +E_TUXDRV_BUSY = E_TUXDRV_BEGIN + 8 +E_TUXDRV_WAVSIZEEXCEDED = E_TUXDRV_BEGIN + 9 + +SW_ID_FLIPPERS_POSITION = 0 +SW_ID_FLIPPERS_REMAINING_MVM = 1 +SW_ID_SPINNING_DIRECTION = 2 +SW_ID_SPINNING_REMAINING_MVM = 3 +SW_ID_LEFT_WING_BUTTON = 4 +SW_ID_RIGHT_WING_BUTTON = 5 +SW_ID_HEAD_BUTTON = 6 +SW_ID_REMOTE_BUTTON = 7 +SW_ID_MOUTH_POSITION = 8 +SW_ID_MOUTH_REMAINING_MVM = 9 +SW_ID_EYES_POSITION = 10 +SW_ID_EYES_REMAINING_MVM = 11 +SW_ID_DESCRIPTOR_COMPLETE = 12 +SW_ID_RF_STATE = 13 +SW_ID_DONGLE_PLUG = 14 +SW_ID_CHARGER_STATE = 15 +SW_ID_BATTERY_LEVEL = 16 +SW_ID_BATTERY_STATE = 17 +SW_ID_LIGHT_LEVEL = 18 +SW_ID_LEFT_LED_STATE = 19 +SW_ID_RIGHT_LED_STATE = 20 +SW_ID_CONNECTION_QUALITY = 21 +SW_ID_AUDIO_FLASH_PLAY = 22 +SW_ID_AUDIO_GENERAL_PLAY = 23 +SW_ID_FLASH_PROG_CURR_TRACK = 24 +SW_ID_FLASH_PROG_LAST_TRACK_SIZE = 25 +SW_ID_TUXCORE_SYMBOLIC_VERSION = 26 +SW_ID_TUXAUDIO_SYMBOLIC_VERSION = 27 +SW_ID_FUXUSB_SYMBOLIC_VERSION = 28 +SW_ID_FUXRF_SYMBOLIC_VERSION = 29 +SW_ID_TUXRF_SYMBOLIC_VERSION = 30 +SW_ID_DRIVER_SYMBOLIC_VERSION = 31 +SW_ID_SOUND_REFLASH_BEGIN = 32 +SW_ID_SOUND_REFLASH_END = 33 +SW_ID_SOUND_REFLASH_CURRENT_TRACK = 34 +SW_ID_EYES_MOTOR_ON = 35 +SW_ID_MOUTH_MOTOR_ON = 36 +SW_ID_FLIPPERS_MOTOR_ON = 37 +SW_ID_SPIN_LEFT_MOTOR_ON = 38 +SW_ID_SPIN_RIGHT_MOTOR_ON = 39 +SW_ID_FLASH_SOUND_COUNT = 40 + +SW_NAME_DRIVER = [ + "flippers_position", + "flippers_remaining_movements", + "spinning_direction", + "spinning_remaining_movements", + "left_wing_button", + "right_wing_button", + "head_button", + "remote_button", + "mouth_position", + "mouth_remaining_movements", + "eyes_position", + "eyes_remaining_movements", + "descriptor_complete", + "radio_state", + "dongle_plug", + "charger_state", + "battery_level", + "battery_state", + "light_level", + "left_led_state", + "right_led_state", + "connection_quality", + "audio_flash_play", + "audio_general_play", + "flash_programming_current_track", + "flash_programming_last_track_size", + "tuxcore_symbolic_version", + "tuxaudio_symbolic_version", + "fuxusb_symbolic_version", + "fuxrf_symbolic_version", + "tuxrf_symbolic_version", + "driver_symbolic_version", + "sound_reflash_begin", + "sound_reflash_end", + "sound_reflash_current_track", + "eyes_motor_on", + "mouth_motor_on", + "flippers_motor_on", + "spin_left_motor_on", + "spin_right_motor_on", + "sound_flash_count" +] + +LOG_LEVEL_DEBUG = 0 +LOG_LEVEL_INFO = 1 +LOG_LEVEL_WARNING = 2 +LOG_LEVEL_ERROR = 3 +LOG_LEVEL_NONE = 4 + +LOG_TARGET_TUX = 0 +LOG_TARGET_SHELL = 1 + +TUX_DRIVER_STATUS_CALLBACK = CFUNCTYPE(None, c_char_p) +TUX_DRIVER_SIMPLE_CALLBACK = CFUNCTYPE(None) + +class TuxDrv(object): + + def __init__(self, library_path): + self.__callback_container = [] + self.tux_driver_lib = None + if os.path.isfile(library_path): + try: + self.tux_driver_lib = CDLL(library_path) + except: + self.tux_driver_lib = None + + def SetStatusCallback(self, funct = None): + if self.tux_driver_lib == None: + return + + if funct == None: + return + + cb = TUX_DRIVER_STATUS_CALLBACK(funct) + self.__callback_container.append(cb) + self.tux_driver_lib.TuxDrv_SetStatusCallback(cb) + return + + def SetEndCycleCallback(self, funct = None): + if self.tux_driver_lib == None: + return + + if funct == None: + return + + cb = TUX_DRIVER_SIMPLE_CALLBACK(funct) + self.__callback_container.append(cb) + self.tux_driver_lib.TuxDrv_SetEndCycleCallback(cb) + return + + def SetDongleConnectedCallback(self, funct = None): + if self.tux_driver_lib == None: + return + + if funct == None: + return + + cb = TUX_DRIVER_SIMPLE_CALLBACK(funct) + self.__callback_container.append(cb) + self.tux_driver_lib.TuxDrv_SetDongleConnectedCallback(cb) + return + + def SetDongleDisconnectedCallback(self, funct = None): + if self.tux_driver_lib == None: + return + + if funct == None: + return + + cb = TUX_DRIVER_SIMPLE_CALLBACK(funct) + self.__callback_container.append(cb) + self.tux_driver_lib.TuxDrv_SetDongleDisconnectedCallback(cb) + return + + def Start(self): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_Start() + + def Stop(self): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_Stop() + + + def PerformCommand(self, delay, command): + if self.tux_driver_lib == None: + return E_TUXDRV_PARSERISDISABLED + + ret = self.tux_driver_lib.TuxDrv_PerformCommand(c_double(delay), + c_char_p(command)) + + return ret + + def PerformMacroFile(self, file_path = ""): + if self.tux_driver_lib == None: + return E_TUXDRV_PARSERISDISABLED + + ret = self.tux_driver_lib.TuxDrv_PerformMacroFile(c_char_p(file_path)) + + return ret + + def PerformMacroText(self, macro = ""): + if self.tux_driver_lib == None: + return E_TUXDRV_PARSERISDISABLED + + ret = self.tux_driver_lib.TuxDrv_PerformMacroText(c_char_p(macro)) + + return ret + + def ClearCommandStack(self): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_ClearCommandStack() + + return + + def SoundReflash(self, tracks = ""): + if self.tux_driver_lib == None: + return E_TUXDRV_BUSY + + ret = self.tux_driver_lib.TuxDrv_SoundReflash(c_char_p(tracks)) + + return ret + + def GetStatusId(self, name = "battery_level"): + if self.tux_driver_lib == None: + return -1 + + idc = c_int(0) + idcp = pointer(idc) + ret = self.tux_driver_lib.TuxDrv_GetStatusId(c_char_p(name), idcp) + + if ret != E_TUXDRV_NOERROR: + idc.value = -1 + + return idc.value + + def GetStatusName(self, id = 0): + if self.tux_driver_lib == None: + return "UNDEFINED" + + result = " " * 256 + ret = self.tux_driver_lib.TuxDrv_GetStatusName(c_int(id), + c_char_p(result)) + result = result.replace(" ", "") + + if ret == E_TUXDRV_NOERROR: + return result + else: + return "UNDEFINED" + + def SetLogLevel(self, level = LOG_LEVEL_INFO): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_SetLogLevel(c_uint8(level)) + + def SetLogTarget(self, target = LOG_TARGET_SHELL): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_SetLogTarget(c_uint8(target)) + + def GetStatusState(self, id = 0): + if self.tux_driver_lib == None: + return "UNDEFINED" + + result = " " * 256 + ret = self.tux_driver_lib.TuxDrv_GetStatusState(c_int(id), + c_char_p(result)) + result = result.replace(" ", "") + + if ret == E_TUXDRV_NOERROR: + return result + else: + return "UNDEFINED" + + def GetStatusValue(self, id = 0): + if self.tux_driver_lib == None: + return "UNDEFINED" + + result = " " * 256 + ret = self.tux_driver_lib.TuxDrv_GetStatusValue(c_int(id), + c_char_p(result)) + result = result.replace(" ", "") + + if ret == E_TUXDRV_NOERROR: + return result + else: + return "UNDEFINED" + + def GetAllStatusState(self): + if self.tux_driver_lib == None: + return "" + + result = " " * 8182 + self.tux_driver_lib.TuxDrv_GetAllStatusState(c_char_p(result)) + result = result.replace(" ", "") + + return result + + def TokenizeStatus(self, status = ""): + if self.tux_driver_lib == None: + return [] + + result = status.split(":") + if len(result) == 1: + if result[0] == '': + result = [] + return result + + def ResetPositions(self): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_ResetPositions() + + return + + def ResetDongle(self): + if self.tux_driver_lib == None: + return + + self.tux_driver_lib.TuxDrv_ResetDongle() + + return + + def GetStatusStruct(self, status = ""): + result = { + 'name' : "None", + 'value' : None, + 'delay' : 0.0, + 'type' : 'string' + } + + if self.tux_driver_lib == None: + return result + + status_s = self.TokenizeStatus(status) + if len(status_s) == 0: + return result + + result['name'] = status_s[0] + result['delay'] = status_s[3] + result['type'] = status_s[1] + + if status_s[1] in ['uint8', 'int8', 'int', 'float', 'bool']: + result['value'] = eval(status_s[2]) + elif status_s[1] == 'string': + result['value'] = status_s[2] + + return result + + def StrError(self, error_code): + if self.tux_driver_lib == None: + return "Shared library not found" + + result = self.tux_driver_lib.TuxDrv_StrError(c_int(error_code)) + + return c_char_p(result).value + +if __name__ == "__main__": + + def on_status_event(status): + status_struct = tux_drv.GetStatusStruct(status) + print status_struct + + def on_dongle_connected(): + tux_drv.ResetPositions() + print tux_drv.GetAllStatusState() + print tux_drv.GetStatusName(0) + print tux_drv.GetStatusValue(0) + print tux_drv.GetStatusState(0) + + if os.name == 'nt': + tux_drv = TuxDrv('../win32/libtuxdriver.dll') + else: + tux_drv = TuxDrv('../unix/libtuxdriver.so') + + tux_drv.SetLogLevel(LOG_LEVEL_INFO) + tux_drv.SetStatusCallback(on_status_event) + tux_drv.SetDongleConnectedCallback(on_dongle_connected) + tux_drv.Start() diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..0a339d7 --- /dev/null +++ b/src/log.c @@ -0,0 +1,255 @@ +/* + * Tux Droid - Log + * Copyright (C) 2007 C2ME S.A. + * + * 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, 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. + */ + +/** + * \file log.c + * \brief Logger functions. + * \ingroup logger + */ + +#include +#include +#include +#include + +#include "log.h" + +/** Name of log file for target LOG_TARGET_TUX */ +#ifdef WIN32 +# define LOG_FILE "c:\\windows\\libtuxdriver.log" +#else +# define LOG_FILE "/var/log/tuxdroid/libtuxdriver.log" +#endif + +/** All logged messages are prefixed with this text */ +#define LOG_PREFIX "libtuxdriver" + +/** Current logging level */ +static log_level_t current_level = LOG_LEVEL_INFO; + +/** Logging level names */ +static const char *level_names[] = +{ + [LOG_LEVEL_DEBUG] = "debug", + [LOG_LEVEL_INFO] = "info", + [LOG_LEVEL_WARNING] = "warning", + [LOG_LEVEL_ERROR] = "error", + [LOG_LEVEL_NONE] = "none" +}; + +/** Current logging target */ +static log_target_t log_target = LOG_TARGET_SHELL; + +/** Log file for target LOG_TARGET_TUX */ +static FILE *log_file; + +/** Whether the log has been opened */ +static bool log_opened; + +/** + * \brief Open the log. + * \param target Logging target. + * \return true if successfull, false otherwise. + */ +bool +log_open(log_target_t target) +{ + if (log_opened) + { + return true; + } + + switch (target) + { + case LOG_TARGET_TUX: + log_file = fopen(LOG_FILE, "w"); + if (log_file == NULL) + { + return false; + } + fclose(log_file); + log_file = NULL; + break; + + case LOG_TARGET_SHELL: + break; + } + + log_target = target; + log_opened = true; + + return true; +} + +/** + * \brief Close the log. + */ +void +log_close(void) +{ + if (!log_opened) + { + return; + } + + switch (log_target) + { + case LOG_TARGET_TUX: + break; + + case LOG_TARGET_SHELL: + break; + } + + log_opened = false; +} + +/** + * \brief Write text in the log. + * \param text Text to write. + */ +static void +write_log_text(char *text) +{ + log_file = fopen(LOG_FILE, "a"); + if (log_file == NULL) + { + return; + } + fprintf(log_file, "%s\n", text); + fclose(log_file); + log_file = NULL; +} + +/** + * \brief Set the logging level. + * \param new_level New logging level. + */ +void +log_set_level(log_level_t new_level) +{ + assert(new_level >= LOG_LEVEL_DEBUG && new_level <= LOG_LEVEL_NONE); + current_level = new_level; +} + +/** + * \brief Get the logging level. + * \return The current logging level + */ +log_level_t +log_get_level(void) +{ + return current_level; +} + +/** + * \brief Log formatted message at the specified level. + * + * \param at_level Level to log the message at. + * \param fmt Message format. + * \param ... Optional message data. + * + * If the priority of the specifed level is lower than the priority + * of the current logging level, the message is silently dropped. + * + * \return true if successful, false otherwise. + */ +bool +log_text(log_level_t at_level, const char *fmt, ...) +{ + char text[1024], *p = text; + size_t size = sizeof(text); + time_t now; + va_list al; + int r; + + /* No need for the log to be 'opened' when logging to std{out,err} */ + if (log_target != LOG_TARGET_SHELL && !log_opened) + { + return false; + } + + /* Logging at level NONE has no sense */ + assert(at_level >= LOG_LEVEL_DEBUG && at_level < LOG_LEVEL_NONE); + + if (at_level < current_level) + { + return true; + } + + /* Add date & time when LOG_TARGET_TUX */ + if (log_target == LOG_TARGET_TUX) + { + now = time(NULL); +#ifdef WIN32 + r = strftime(p, size, "%y-%m-%d %H-%M-%S ", localtime(&now)); +#else + r = strftime(p, size, "%F %T ", localtime(&now)); +#endif + if (r == 0) + { + return false; + } + + p += r; + size -= r; + } + + /* Only prefix non-INFO level messages */ + if (at_level != LOG_LEVEL_INFO) + { + r = snprintf(p, size, "%s: ", level_names[at_level]); + if (r < 0) + { + return false; + } + + p += r; + size -= r; + } + + va_start(al, fmt); + r = vsnprintf(p, size, fmt, al); + va_end(al); + if (r < 0) + { + return false; + } + + switch (log_target) + { + case LOG_TARGET_TUX: + write_log_text(text); + break; + + case LOG_TARGET_SHELL: + if (at_level == LOG_LEVEL_WARNING || at_level == LOG_LEVEL_ERROR) + { + fprintf(stderr, "%s: %s\n", LOG_PREFIX, text); + } + else + { + fprintf(stdout, "%s: %s\n", LOG_PREFIX, text); + } + break; + } + + return true; +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..b9daef7 --- /dev/null +++ b/src/log.h @@ -0,0 +1,63 @@ +/* + * Tux Droid - Log + * Copyright (C) 2007 C2ME S.A. + * + * 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, 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. + */ + +/** + * \file log.h + * \brief Logger header. + * \ingroup logger + */ + +#ifndef __LOG_H__ +#define __LOG_H__ + +#include + +/** \brief Logging target. */ +typedef enum log_target +{ + LOG_TARGET_TUX, /**< Target file */ + LOG_TARGET_SHELL /**< Target shell */ +} log_target_t; + +extern bool log_open(log_target_t target); +extern void log_close(void); + +/** \brief Logging levels, in increasing priorities */ +typedef enum log_level +{ + LOG_LEVEL_DEBUG, /**< Level Debug */ + LOG_LEVEL_INFO, /**< Level Info */ + LOG_LEVEL_WARNING, /**< Level Warning */ + LOG_LEVEL_ERROR, /**< Level Error */ + LOG_LEVEL_NONE /**< Level None */ +} log_level_t; + +extern void log_set_level(log_level_t new_level); +extern log_level_t log_get_level(void); + +extern bool log_text(log_level_t at_level, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +#define log_debug(fmt, ...) log_text(LOG_LEVEL_DEBUG, (fmt), ## __VA_ARGS__) +#define log_info(fmt, ...) log_text(LOG_LEVEL_INFO, (fmt), ## __VA_ARGS__) +#define log_warning(fmt, ...) log_text(LOG_LEVEL_WARNING, (fmt), ## __VA_ARGS__) +#define log_error(fmt, ...) log_text(LOG_LEVEL_ERROR, (fmt), ## __VA_ARGS__) + +#endif /* __LOG_H__ */ diff --git a/src/svnrev.tmpl.h b/src/svnrev.tmpl.h new file mode 100644 index 0000000..c2191ed --- /dev/null +++ b/src/svnrev.tmpl.h @@ -0,0 +1,37 @@ +/* + * Tux Droid - svnrev template + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ +/* SVN : $Id$ */ +/* This file is a template to generate svnrev.h automatically. + * SubWCRev performs keyword susbstitution whith SVN information. + */ + +#ifndef _SVNREV_H_ +#define _SVNREV_H_ + +#define SVN_REV $WCREV$ +#define SVN_REVSTR "$WCREV$" +#define SVN_REVDATE "$WCDATE$" +#define SVN_MOD $WCMODS?1:0$ +#define SVN_MIX $WCMIXED?1:0$ +#define SVN_STATUS (SVN_MOD + (SVN_MIX<<1)) +#define SVN_RANGE "$WCRANGE$" +#define SVN_URL "$WCURL$" + +#endif /* _SVNREV_H_ */ diff --git a/src/threading_uniform.h b/src/threading_uniform.h new file mode 100644 index 0000000..37bb416 --- /dev/null +++ b/src/threading_uniform.h @@ -0,0 +1,54 @@ +/* + * FICHIER .H pour la realisation de THREADS PORTABLES LINUX/WINDOWS + * + * ce code a été écrit par Smeagol(smeagol-worms4@ifrance.com) et revu par + * Jerry Kan (pilouface@gmail.com) + * pour le site + * http://www.irrlicht.fr/ communauté francaise Irrlicht + * + * retrouvez ce tutoriel et beaucoup d'autres sur notre forum, + */ + +#ifdef USE_MUTEX +#ifndef _THREADING_UNIFORM_H_ +#define _THREADING_UNIFORM_H_ + +#ifdef WIN32 +# include +# define callback_t unsigned long __stdcall +# define thread_t HANDLE +# define thread_create(thrd, fct, param) thrd = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(fct),(param),0,NULL) +# define thread_delete(thrd) CloseHandle(thrd); +# define thread_wait_close(thrd) WaitForMultipleObjects(1, &thrd, TRUE, INFINITE) +# define mutex_t CRITICAL_SECTION +# define mutex_init(mutex) InitializeCriticalSection(& mutex) +# define mutex_lock(mutex) EnterCriticalSection(& mutex) +# define mutex_unlock(mutex) LeaveCriticalSection(& mutex) +# define mutex_delete(mutex) DeleteCriticalSection(& mutex) +# define semaphore_t HANDLE +# define semaphore_init(sema, max, place) ((sema) = CreateSemaphore(NULL, (max), (place), NULL)) +# define semaphore_lock(sema) WaitForSingleObject((sema), INFINITE) +# define semaphore_unlock(sema) ReleaseSemaphore((sema), 1, NULL) +# define semaphore_delete(sema) CloseHandle(sema) +#else +# include +# define callback_t void * +# define thread_t pthread_t +# define thread_create(thrd, fct, param) pthread_create(&thrd, NULL, (fct), ((void *)param)); +# define thread_delete(thrd) +# define thread_wait_close(thrd) pthread_join(thrd, NULL) +# include +# define mutex_t pthread_mutex_t +# define mutex_init(mutex) pthread_mutex_init ((&mutex), NULL) +# define mutex_lock(mutex) pthread_mutex_lock((&mutex)) +# define mutex_unlock(mutex) pthread_mutex_unlock((&mutex)) +# define mutex_delete(mutex) pthread_mutex_destroy((&mutex)) +# define semaphore_t sem_t* +# define semaphore_init(sema, max, place) (sema) = new sem_t; sem_init ((sema), (max), (place)) +# define semaphore_lock(sema) sem_wait((sema)) +# define semaphore_unlock(sema) sem_post((sema)) +# define semaphore_delete(sema) sem_destroy((sema)); delete ((sema)) +#endif + +#endif +#endif diff --git a/src/tux_audio.c b/src/tux_audio.c new file mode 100644 index 0000000..1c40cb0 --- /dev/null +++ b/src/tux_audio.c @@ -0,0 +1,76 @@ +/* + * Tux Droid - Audio + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_audio.c + * \brief Audio speaker functions. + * \author remi.jocaille@c2me.be + * \ingroup audio + */ + +#include "tux_audio.h" +#include "tux_hw_cmd.h" +#include "tux_types.h" +#include "tux_usb.h" + +/** + * \brief Send a command to switch the audio stream to Tuxdroid-Audio channel. + * \return The sending success. + */ +LIBLOCAL bool +tux_audio_cmd_channel_general(void) +{ + data_frame frame = {USB_DONGLE_AUDIO_CMD, 0, 0, 0}; + return tux_usb_send_to_dongle(frame); +} + +/** + * \brief Send a command to switch the audio stream to Tuxdroid-TTS channel. + * \return The sending success. + */ +LIBLOCAL bool +tux_audio_cmd_channel_tts(void) +{ + data_frame frame = {USB_DONGLE_AUDIO_CMD, 1, 0, 0}; + return tux_usb_send_to_dongle(frame); +} + +/** + * \brief Send a command to mute/unmute the speaker. + * \param value Mute value. + * \return The sending success. + */ +LIBLOCAL bool +tux_audio_cmd_mute(bool value) +{ + data_frame frame = {AUDIO_MUTE_CMD, 0, 0, 0}; + + if (value) + { + frame[1] = 1; + } + else + { + frame[1] = 0; + } + + return tux_usb_send_to_tux(frame); +} + diff --git a/src/tux_audio.h b/src/tux_audio.h new file mode 100644 index 0000000..044ef41 --- /dev/null +++ b/src/tux_audio.h @@ -0,0 +1,37 @@ +/* + * Tux Droid - Audio + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_audio.h + * \brief Audio speaker header. + * \author remi.jocaille@c2me.be + * \ingroup audio + */ + +#ifndef _TUX_AUDIO_H_ +#define _TUX_AUDIO_H_ + +#include + +extern bool tux_audio_cmd_channel_general(void); +extern bool tux_audio_cmd_channel_tts(void); +extern bool tux_audio_cmd_mute(bool value); + +#endif /* _TUX_AUDIO_H_ */ diff --git a/src/tux_battery.c b/src/tux_battery.c new file mode 100644 index 0000000..2a6ad58 --- /dev/null +++ b/src/tux_battery.c @@ -0,0 +1,128 @@ +/* + * Tux Droid - Battery + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_battery.c + * \brief Battery level functions. + * \author remi.jocaille@c2me.be + * \ingroup power + */ + +#include + +#include "tux_battery.h" +#include "tux_misc.h" +#include "tux_hw_status.h" +#include "tux_sw_status.h" + +/** \brief Battery states */ +typedef enum +{ + FULL, /**< Battery level full */ + HIGH, /**< Battery level high */ + LOW, /**< Battery level low */ + EMPTY /**< Battery level empty */ +} battery_state_t; + +/** + * REPORTING_DELTA is the treshold value for changes; + * changes smaller than this value are not reported + * might become a controllable parameter in the future + */ +#define REPORTING_DELTA 100 + +/** Current battery state */ +static battery_state_t battery_state = EMPTY; +/** Current battery state for event */ +static int last_level_for_event = 0; + +/** + * \brief Update the status of the battery voltage. + */ +LIBLOCAL void +tux_battery_update_level(void) +{ + int new_level = 0; + int adc_value; + char *new_state_str = ""; + int delta; + + /* Get the current battery level from adc and convert it to mV */ + adc_value = (hw_status_table.battery.high_level << 8); + adc_value += hw_status_table.battery.low_level; + new_level = adc_value * 7.467; + /* 7.467 = 0.00322 * 2.319 * 1000 no idea where the first two are from + * 1000 is to go to mV */ + + /* Get the difference between last and new level */ + delta = new_level - last_level_for_event; + + /* If the delta threshold is reached */ + if ((delta > REPORTING_DELTA) || (delta < -REPORTING_DELTA)) + { + last_level_for_event = new_level; + + /* Update directly the SW_ID_BATTERY_LEVEL status if motors off */ + if (!hw_status_table.battery.motors_state) + { + tux_sw_status_set_intvalue(SW_ID_BATTERY_LEVEL, new_level, true); + } + + /* Battery level full */ + if (new_level >= TUX_BATTERY_FULL_VALUE) + { + new_state_str = STRING_VALUE_FULL; + battery_state = FULL; + } + else + { + /* Battery level high */ + if ((new_level < TUX_BATTERY_FULL_VALUE) && + (new_level >= TUX_BATTERY_HIGH_VALUE)) + { + new_state_str = STRING_VALUE_HIGH; + battery_state = HIGH; + } + else + { + /* Battery level low */ + if ((new_level < TUX_BATTERY_HIGH_VALUE) && + (new_level >= TUX_BATTERY_LOW_VALUE)) + { + new_state_str = STRING_VALUE_LOW; + battery_state = LOW; + } + else + { + /* Battery level empty */ + new_state_str = STRING_VALUE_EMPTY; + battery_state = EMPTY; + } + } + } + /* Update the SW_ID_BATTERY_STATE status */ + tux_sw_status_set_strvalue(SW_ID_BATTERY_STATE, new_state_str, true); + } + else + { + /* Update the SW_ID_BATTERY_LEVEL status */ + tux_sw_status_set_intvalue(SW_ID_BATTERY_LEVEL, new_level, false); + } +} diff --git a/src/tux_battery.h b/src/tux_battery.h new file mode 100644 index 0000000..5cfffb8 --- /dev/null +++ b/src/tux_battery.h @@ -0,0 +1,47 @@ +/* + * Tux Droid - Battery + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_battery.c + * \brief Battery level header. + * \author remi.jocaille@c2me.be + * \ingroup power + */ + +#ifndef _TUX_BATTERY_H_ +#define _TUX_BATTERY_H_ + +/** + * \name Battery level thresholds in mV. + * @{ + */ +/** Battery level threshold FULL */ +#define TUX_BATTERY_FULL_VALUE 6000 +/** Battery level threshold HIGH */ +#define TUX_BATTERY_HIGH_VALUE 5400 +/** Battery level threshold LOW */ +#define TUX_BATTERY_LOW_VALUE 4800 +/** Battery level threshold EMPTY */ +#define TUX_BATTERY_EMPTY_VALUE 0000 +/** @} */ + +extern void tux_battery_update_level(void); + +#endif /* _TUX_BATTERY_H_ */ diff --git a/src/tux_cmd_parser.c b/src/tux_cmd_parser.c new file mode 100644 index 0000000..5a53a93 --- /dev/null +++ b/src/tux_cmd_parser.c @@ -0,0 +1,1434 @@ +/* + * Tux Droid - Command parser + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_cmd_parser.c + * \brief Command parser functions. + * \author remi.jocaille@c2me.be + * \ingroup command_parser + */ + +#include +#include +#include + +#ifdef USE_MUTEX +# include "threading_uniform.h" +#endif + +#include "log.h" +#include "tux_audio.h" +#include "tux_cmd_parser.h" +#include "tux_error.h" +#include "tux_eyes.h" +#include "tux_leds.h" +#include "tux_mouth.h" +#include "tux_sound_flash.h" +#include "tux_spinning.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" +#include "tux_user_inputs.h" +#include "tux_flippers.h" + +#define NRCMDS 512 + +/** \brief Cmd stack structure */ +typedef struct { + delay_cmd_t cmd_list[NRCMDS]; /**< Stack */ + int cmd_count; /**< Number of commands in stack */ +} cmd_stack_t; + +/** \brief Cmd stack for user */ +static cmd_stack_t user_cmd_stack; +/** \brief Cmd stack for internal system */ +static cmd_stack_t sys_cmd_stack; +#ifdef USE_MUTEX +static mutex_t __stack_mutex; +static mutex_t __macro_mutex; +#endif +/** \brief Flag which indicates if the parser is enabled */ +static bool cmd_parser_enable = true; + +/** + * \brief Initialize the parser. + */ +LIBLOCAL void +tux_cmd_parser_init(void) +{ + memset(&user_cmd_stack, 0, sizeof(cmd_stack_t)); + memset(&sys_cmd_stack, 0, sizeof(cmd_stack_t)); +#ifdef USE_MUTEX + mutex_init(__stack_mutex); + mutex_init(__macro_mutex); +#endif +} + +/** + * \brief Enabling/disabling the parser. + * \param value Flag value. + */ +LIBLOCAL void +tux_cmd_parser_set_enable(bool value) +{ + cmd_parser_enable = value; +} + +/** + * \brief Get tokens from a string line. + * \param src_str Line to parse. + * \param toks Output tokens. + * \param max_tokens Maximum tokens to retrieve. + * \param delimiters Delimiters chars. + * \return The number of retrieved tokens. + */ +LIBLOCAL int +tux_cmd_parser_get_tokens(const char *src_str, tokens_t *toks, + int max_tokens, const char *delimiters) +{ + const char *p; /* pointer to the next token */ + const char *pnext; /* pointer to the next delimiter */ + int len; /* length of the next token */ + int cnt = 0; /* nr of tokens processed */ + + p = src_str; + + /* the implementation below is build upon strncpy + it might be more efficient/cleaner to use strtok + */ + + if (p) + { + while (1) + { + /* get the next delimiter */ + pnext = strpbrk(p, delimiters); + if (pnext) + { + len = pnext - p; + strncpy((*toks)[cnt], p, len); + ((*toks)[cnt])[len] = 0; + cnt++; + if (cnt >= max_tokens) + { + break; + } + } + else + { + /* no next delimiter, so copy all the remaining strings */ + strcpy((*toks)[cnt], p); + cnt++; + break; + } + p = pnext+1; + } + } + return(cnt); +} + +/** + * \brief Convert a string to a final movement state value. + * \brief conststr String to convert. + * \brief state Output final movement state. + * \return The convertion success. + */ +static bool +str_to_state_t(const char *conststr, move_final_state_t *state) +{ + if (!strcmp(conststr, "NDEF")) + { + *state = FINAL_ST_UNDEFINED; + return true; + } + if (!strcmp(conststr, "UNDEFINED")) + { + *state = FINAL_ST_UNDEFINED; + return true; + } + if (!strcmp(conststr, "OPEN")) + { + *state = FINAL_ST_OPEN_UP; + return true; + } + if (!strcmp(conststr, "UP")) + { + *state = FINAL_ST_OPEN_UP; + return true; + } + if (!strcmp(conststr, "CLOSE")) + { + *state = FINAL_ST_CLOSE_DOWN; + return true; + } + if (!strcmp(conststr, "DOWN")) + { + *state = FINAL_ST_CLOSE_DOWN; + return true; + } + if (!strcmp(conststr, "STOP")) + { + *state = FINAL_ST_STOP; + return true; + } + return false; +} + +/** + * \brief Convert a string to a led type. + * \brief conststr String to convert. + * \brief leds Output led type. + * \return The convertion success. + */ +static bool +str_to_leds_t(const char *conststr, leds_t *leds) +{ + if (!strcmp(conststr, "LED_NONE")) + { + *leds = LED_NONE; + return true; + } + if (!strcmp(conststr, "LED_LEFT")) + { + *leds = LED_LEFT; + return true; + } + if (!strcmp(conststr, "LED_RIGHT")) + { + *leds = LED_RIGHT; + return true; + } + if (!strcmp(conststr, "LED_BOTH")) + { + *leds = LED_BOTH; + return true; + } + return false; +} + +/** + * \brief Convert a string to a led effect type. + * \brief conststr String to convert. + * \brief effect_type Output led effect type. + * \return The convertion success. + */ +static bool +str_to_effect_type(const char *conststr, effect_type_t *effect_type) +{ + if (!strcmp(conststr, "UNAFFECTED")) + { + *effect_type = UNAFFECTED; + return true; + } + if (!strcmp(conststr, "LAST")) + { + *effect_type = LAST; + return true; + } + if (!strcmp(conststr, "NONE")) + { + *effect_type = NONE; + return true; + } + if (!strcmp(conststr, "DEFAULT")) + { + *effect_type = DEFAULT; + return true; + } + if (!strcmp(conststr, "FADE_DURATION")) + { + *effect_type = FADE_DURATION; + return true; + } + if (!strcmp(conststr, "FADE_RATE")) + { + *effect_type = FADE_RATE; + return true; + } + if (!strcmp(conststr, "GRADIENT_NBR")) + { + *effect_type = GRADIENT_NBR; + return true; + } + if (!strcmp(conststr, "GRADIENT_DELTA")) + { + *effect_type = GRADIENT_DELTA; + return true; + } + return false; +} + +/** + * \brief Parse an audio command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_audio_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "CHANNEL_GENERAL") == 0) + { + cmd->sub_command = CHANNEL_GENERAL; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "CHANNEL_TTS") == 0) + { + cmd->sub_command = CHANNEL_TTS; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "MUTE") == 0) + { + cmd->sub_command = MUTE; + if (str_to_bool(tokens[3], &cmd->audio_mute_parameters.muteflag)) + { + // write to struct + ret = E_TUXDRV_NOERROR; + } + } + return ret; +} + +/** + * \brief Parse an eyes command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_eyes_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "CLOSE") == 0) + { + cmd->sub_command = CLOSE; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "OFF") == 0) + { + cmd->sub_command = OFF; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "ON") == 0) + { + cmd->sub_command = ON; + if (str_to_uint8(tokens[3], &cmd->eyes_on_parameters.nr_movements) && + str_to_state_t(tokens[4], &cmd->eyes_on_parameters.state)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "ON_DURING") == 0) + { + cmd->sub_command = ON_DURING; + if (str_to_float(tokens[3], &cmd->eyes_on_during_parameters.duration) && + str_to_state_t(tokens[4], &cmd->eyes_on_during_parameters.state)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "OPEN") == 0) + { + cmd->sub_command = OPEN; + ret = E_TUXDRV_NOERROR; + } + return ret; +} + +/** + * \brief Parse an IR command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_ir_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "ON") == 0) + { + cmd->sub_command = ON; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "OFF") == 0) + { + cmd->sub_command = OFF; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "SEND") == 0) + { + cmd->sub_command = SEND; + if (str_to_uint8(tokens[3], &cmd->ir_send_parameters.address) && + str_to_uint8(tokens[4], &cmd->ir_send_parameters.command)) + { + ret = E_TUXDRV_NOERROR; + } + } + + return ret; +} + +/** + * \brief Parse a led command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_led_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + if (strcmp(tokens[2], "BLINK") == 0) + { + cmd->sub_command = BLINK; + if (str_to_leds_t(tokens[3], &cmd->led_blink_parameters.leds) && + str_to_uint8(tokens[4], &cmd->led_blink_parameters.pulse_count) && + str_to_float(tokens[5], &cmd->led_blink_parameters.pulse_period)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "OFF") == 0) + { + cmd->sub_command = OFF; + if (str_to_leds_t(tokens[3], &cmd->led_off_parameters.leds)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "ON") == 0) + { + cmd->sub_command = ON; + if (str_to_leds_t(tokens[3], &cmd->led_on_parameters.leds) && + str_to_float(tokens[4], &cmd->led_on_parameters.intensity)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "PULSE") == 0) + { + cmd->sub_command = PULSE; + if (str_to_leds_t(tokens[3], &cmd->led_pulse_parameters.leds) && + str_to_float(tokens[4], &cmd->led_pulse_parameters.min_intensity) && + str_to_float(tokens[5], &cmd->led_pulse_parameters.max_intensity) && + str_to_uint8(tokens[6], &cmd->led_pulse_parameters.pulse_count) && + str_to_float(tokens[7], &cmd->led_pulse_parameters.pulse_period) && + str_to_effect_type(tokens[8], &cmd->led_pulse_parameters.effect_type) && + str_to_float(tokens[9], &cmd->led_pulse_parameters.effect_speed) && + str_to_uint8(tokens[10], &cmd->led_pulse_parameters.effect_step)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "SET") == 0) + { + cmd->sub_command = SET; + if (str_to_leds_t(tokens[3], &cmd->led_set_parameters.leds) && + str_to_float(tokens[4], &cmd->led_set_parameters.intensity) && + str_to_effect_type(tokens[5], &cmd->led_set_parameters.effect_type) && + str_to_float(tokens[6], &cmd->led_set_parameters.effect_speed) && + str_to_uint8(tokens[7], &cmd->led_set_parameters.effect_step)) + { + ret = E_TUXDRV_NOERROR; + } + } + return ret; +} + +/** + * \brief Parse a mouth command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_mouth_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "CLOSE") == 0) + { + cmd->sub_command = CLOSE; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "OFF") == 0) + { + cmd->sub_command = OFF; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "ON") == 0) + { + cmd->sub_command = ON; + if (str_to_uint8(tokens[3], &cmd->mouth_on_parameters.nr_movements) && + str_to_state_t(tokens[4], &cmd->mouth_on_parameters.state)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "ON_DURING") == 0) + { + cmd->sub_command = ON_DURING; + if (str_to_float(tokens[3], &cmd->mouth_on_during_parameters.duration) && + str_to_state_t(tokens[4], &cmd->mouth_on_during_parameters.state)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "OPEN") == 0) + { + cmd->sub_command = OPEN; + ret = E_TUXDRV_NOERROR; + } + return ret; +} + +/** + * \brief Parse a sound flash command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_sound_flash_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "PLAY") == 0) + { + cmd->sub_command = PLAY; + if (str_to_uint8(tokens[3], &cmd->sound_flash_play_parameters.track) && + str_to_float(tokens[4], &cmd->sound_flash_play_parameters.volume)) + { + ret = E_TUXDRV_NOERROR; + } + } + return ret; +} + +/** + * \brief Parse a spinning command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_spinning_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "LEFT_ON") == 0) + { + cmd->sub_command = LEFT_ON; + if (str_to_uint8(tokens[3], &cmd->spinning_on_parameters.nr_qturns)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "LEFT_ON_DURING") == 0) + { + cmd->sub_command = LEFT_ON_DURING; + if (str_to_float(tokens[3], &cmd->spinning_on_during_parameters.duration)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "RIGHT_ON") == 0) + { + cmd->sub_command = RIGHT_ON; + if (str_to_uint8(tokens[3], &cmd->spinning_on_parameters.nr_qturns)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "RIGHT_ON_DURING") == 0) + { + cmd->sub_command = RIGHT_ON_DURING; + if (str_to_float(tokens[3], &cmd->spinning_on_during_parameters.duration)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "OFF") == 0) + { + cmd->sub_command = OFF; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "SPEED") == 0) + { + cmd->sub_command = SPEED; + if (str_to_uint8(tokens[3], &cmd->spinning_speed_parameters.speed)) + { + ret = E_TUXDRV_NOERROR; + } + } + return ret; +} + +/** + * \brief Parse a flippers command [Level 2] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_flippers_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[2], "DOWN") == 0) + { + cmd->sub_command = DOWN; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "OFF") == 0) + { + cmd->sub_command = OFF; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "ON") == 0) + { + cmd->sub_command = ON; + if (str_to_uint8(tokens[3], &cmd->flippers_on_parameters.nr_movements) && + str_to_state_t(tokens[4], &cmd->flippers_on_parameters.state)) + { + ret = E_TUXDRV_NOERROR; + } + } + else if (strcmp(tokens[2], "ON_DURING") == 0) + { + cmd->sub_command = ON_DURING; + if (str_to_float(tokens[3], &cmd->flippers_on_during_parameters.duration) && + str_to_state_t(tokens[4], &cmd->flippers_on_during_parameters.state)) + { + ret = E_TUXDRV_NOERROR; + } + cmd->sub_command = ON_DURING; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "UP") == 0) + { + cmd->sub_command = UP; + ret = E_TUXDRV_NOERROR; + } + else if (strcmp(tokens[2], "SPEED") == 0) + { + cmd->sub_command = SPEED; + if (str_to_uint8(tokens[3], &cmd->flippers_speed_parameters.speed)) + { + ret = E_TUXDRV_NOERROR; + } + } + return ret; +} + +/** + * \brief Parse a Tux command [Level 1] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_tux_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + + if (strcmp(tokens[1], "AUDIO") == 0) + { + cmd->command = AUDIO; + ret = parse_tux_audio_command(tokens, cmd); + } + else if (strcmp(tokens[1], "EYES") == 0) + { + cmd->command = EYES; + ret = parse_tux_eyes_command(tokens, cmd); + } + else if (strcmp(tokens[1], "IR") == 0) + { + cmd->command = IR; + ret = parse_tux_ir_command(tokens, cmd); + } + else if (strcmp(tokens[1], "LED") == 0) + { + cmd->command = LED; + ret = parse_tux_led_command(tokens, cmd); + } + else if (strcmp(tokens[1], "MOUTH") == 0) + { + cmd->command = MOUTH; + ret = parse_tux_mouth_command(tokens, cmd); + } + else if (strcmp(tokens[1], "SOUND_FLASH") == 0) + { + cmd->command = SOUND_FLASH; + ret = parse_tux_sound_flash_command(tokens, cmd); + } + else if (strcmp(tokens[1], "SPINNING") == 0) + { + cmd->command = SPINNING; + ret = parse_tux_spinning_command(tokens, cmd); + } + else if (strcmp(tokens[1], "FLIPPERS") == 0) + { + cmd->command = FLIPPERS; + ret = parse_tux_flippers_command(tokens, cmd); + } + return ret; +} + +/** + * \brief Parse a RAW command [Level 1] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_raw_command(tokens_t tokens, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + int r = 0; + int i; + + for (i = 0; i < TUX_SEND_LENGTH; i++) + { + if (hex_to_uint8(tokens[i + 1], &cmd->raw_parameters.raw[i])) + { + r++; + } + } + + if (r == TUX_SEND_LENGTH) + { + ret = E_TUXDRV_NOERROR; + } + return ret; +} + +/** + * \brief Parse a command [Level 0] + * \param tokens Command tokens. + * \param cmd Cmd structure. + * \return The error result. + */ +static TuxDrvError +parse_command(const char *cmd_str, delay_cmd_t *cmd) +{ + TuxDrvError ret = E_TUXDRV_INVALIDCOMMAND; + tokens_t tokens; + /*int nr_tokens;*/ + + /* If the parser is not enabled then fail */ + if (!cmd_parser_enable) + { + return E_TUXDRV_PARSERISDISABLED; + } + + log_debug("parse_command : [%s]", cmd_str); + memset(&tokens, 0, sizeof(tokens_t)); + /*nr_tokens = tux_cmd_parser_get_tokens(cmd_str, &tokens, 32, ":,"); + */ + + tux_cmd_parser_get_tokens(cmd_str, &tokens, 32, ":,"); + + if (strcmp(tokens[0], "TUX_CMD") == 0) + { + cmd->command_group = TUX_CMD; + ret = parse_tux_command(tokens, cmd); + } + else if (strcmp(tokens[0], "RAW_CMD") == 0) + { + cmd->command_group = RAW_CMD; + ret = parse_raw_command(tokens, cmd); + } + return ret; +} + +/** + * \brief Execute an audio command. + * \param cmd Command to execute. + */ +static void +execute_audio_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case CHANNEL_GENERAL: + tux_audio_cmd_channel_general(); + break; + case CHANNEL_TTS: + tux_audio_cmd_channel_tts(); + break; + case MUTE: + tux_audio_cmd_mute(cmd->audio_mute_parameters.muteflag); + break; + default: /* should not occur */ + log_error("execute invalid audio command"); + } +} + +/** + * \brief Execute an eyes command. + * \param cmd Command to execute. + */ +static void +execute_eyes_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case ON: + tux_eyes_cmd_on( + cmd->eyes_on_parameters.nr_movements, + cmd->eyes_on_parameters.state); + break; + case ON_DURING: + tux_eyes_cmd_on_during( + cmd->eyes_on_during_parameters.duration, + cmd->eyes_on_during_parameters.state); + break; + case OPEN: + tux_eyes_cmd_open(); + break; + case CLOSE: + tux_eyes_cmd_close(); + break; + case OFF: + tux_eyes_cmd_off(); + break; + default: /* should not occur */ + log_error("execute invalid eyes command"); + } +} + +/** + * \brief Execute an IR command. + * \param cmd Command to execute. + */ +static void +execute_ir_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case ON: + tux_user_inputs_cmd_ir_on(); + break; + case OFF: + tux_user_inputs_cmd_ir_off(); + case SEND: + tux_user_inputs_cmd_ir_send( + cmd->ir_send_parameters.address, + cmd->ir_send_parameters.command); + break; + default: /* should not occur */ + log_error("execute invalid ir command"); + } +} + +/** + * \brief Execute a led command. + * \param cmd Command to execute. + */ +static void +execute_led_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case ON: + tux_leds_cmd_set( + cmd->led_on_parameters.leds, + cmd->led_on_parameters.intensity, + NONE, + 0, + 0); + break; + case OFF: + tux_leds_cmd_set( + cmd->led_off_parameters.leds, + 0.0, + NONE, + 0, + 0); + break; + case PULSE: + tux_leds_cmd_pulse( + cmd->led_pulse_parameters.leds, + cmd->led_pulse_parameters.min_intensity, + cmd->led_pulse_parameters.max_intensity, + cmd->led_pulse_parameters.pulse_count, + cmd->led_pulse_parameters.pulse_period, + cmd->led_pulse_parameters.effect_type, + cmd->led_pulse_parameters.effect_speed, + cmd->led_pulse_parameters.effect_step); + break; + case BLINK: + tux_leds_cmd_pulse( + cmd->led_blink_parameters.leds, + 0.0, + 1.0, + cmd->led_blink_parameters.pulse_count, + cmd->led_blink_parameters.pulse_period, + NONE, + 0, + 0); + break; + case SET: + tux_leds_cmd_set( + cmd->led_set_parameters.leds, + cmd->led_set_parameters.intensity, + cmd->led_set_parameters.effect_type, + cmd->led_set_parameters.effect_speed, + cmd->led_set_parameters.effect_step); + break; + default: /* should not occur */ + log_error("execute invalid led command"); + } +} + +/** + * \brief Execute a mouth command. + * \param cmd Command to execute. + */ +static void +execute_mouth_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case ON: + tux_mouth_cmd_on( + cmd->mouth_on_parameters.nr_movements, + cmd->mouth_on_parameters.state); + break; + case ON_DURING: + tux_mouth_cmd_on_during( + cmd->mouth_on_during_parameters.duration, + cmd->mouth_on_during_parameters.state); + break; + case OPEN: + tux_mouth_cmd_open(); + break; + case CLOSE: + tux_mouth_cmd_close(); + break; + case OFF: + tux_mouth_cmd_off(); + break; + default: /* should not occur */ + log_error("execute invalid mouth command"); + } +} + +/** + * \brief Execute a sound flash command. + * \param cmd Command to execute. + */ +static void +execute_sound_flash_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case PLAY: + tux_sound_flash_cmd_play( + cmd->sound_flash_play_parameters.track, + cmd->sound_flash_play_parameters.volume); + break; + default: /* should not occur */ + log_error("execute invalid sound flash command"); + } +} + +/** + * \brief Execute a spinning command. + * \param cmd Command to execute. + */ +static void +execute_spinning_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case LEFT_ON: + tux_spinning_cmd_left_on(cmd->spinning_on_parameters.nr_qturns); + break; + case RIGHT_ON: + tux_spinning_cmd_right_on(cmd->spinning_on_parameters.nr_qturns); + break; + case LEFT_ON_DURING: + tux_spinning_cmd_left_on_during(cmd->spinning_on_during_parameters.duration); + break; + case RIGHT_ON_DURING: + tux_spinning_cmd_right_on_during(cmd->spinning_on_during_parameters.duration); + break; + case OFF: + tux_spinning_cmd_off(); + break; + case SPEED: + tux_spinning_cmd_speed(cmd->spinning_speed_parameters.speed); + break; + default: /* should not occur */ + log_error("execute invalid spinning command"); + } +} + +/** + * \brief Execute a flippers command. + * \param cmd Command to execute. + */ +static void +execute_flippers_command (delay_cmd_t *cmd) +{ + switch (cmd->sub_command) { + case ON: + tux_flippers_cmd_on( + cmd->flippers_on_parameters.nr_movements, + cmd->flippers_on_parameters.state); + break; + case ON_DURING: + tux_flippers_cmd_on_during( + cmd->flippers_on_during_parameters.duration, + cmd->flippers_on_during_parameters.state); + break; + case OFF: + tux_flippers_cmd_off(); + break; + case UP: + tux_flippers_cmd_up(); + break; + case DOWN: + tux_flippers_cmd_down(); + break; + case SPEED: + tux_flippers_cmd_speed(cmd->flippers_speed_parameters.speed); + break; + default: /* should not occur */ + log_error("execute invalid flippers command"); + } +} + +/** + * \brief Execute a RAW command. + * \param cmd Command to execute. + */ +static void +execute_raw_command (delay_cmd_t *cmd) +{ + tux_usb_send_raw(cmd->raw_parameters.raw); +} + +/** + * \brief Execute a command. + * \param cmd Command to execute. + */ +static void +execute_command (delay_cmd_t *cmd) +{ + if(cmd->command_group == TUX_CMD) + { + switch(cmd->command) { + case AUDIO: + execute_audio_command(cmd); + break; + case EYES: + execute_eyes_command(cmd); + break; + case IR: + execute_ir_command(cmd); + break; + case LED: + execute_led_command(cmd); + break; + case MOUTH: + execute_mouth_command(cmd); + break; + case SOUND_FLASH: + execute_sound_flash_command(cmd); + break; + case SPINNING: + execute_spinning_command(cmd); + break; + case FLIPPERS: + execute_flippers_command(cmd); + break; + } + } + else + { + if(cmd->command_group == RAW_CMD) + { + execute_raw_command(cmd); + } + } + cmd->command_group = NO_CMD; +} + +/** + * \brief Cleanup of the system commands in order to keep the consistency of the + * system stack. + * \param command Command to check. + */ +LIBLOCAL void +tux_cmd_parser_clean_sys_command(tux_command_t command) +{ + int i, j; + bool have_parent = true; + + /* For all sys commands */ + for (i = 0; i < NRCMDS; i++) + { + /* System command match */ + if ((sys_cmd_stack.cmd_list[i].command == command) && + (sys_cmd_stack.cmd_list[i].command_group != NO_CMD)) + { + /* Find the user command (on_during) which have insert this + * system command. + */ + have_parent = false; + + for (j = 0; j < NRCMDS; j++) + { + /* Possible user command*/ + if ((user_cmd_stack.cmd_list[j].command == command) && + (user_cmd_stack.cmd_list[j].command_group != NO_CMD)) + { + /* Ok : system command have a parent user command */ + if (user_cmd_stack.cmd_list[j].inserted_at_time ==\ + sys_cmd_stack.cmd_list[i].inserted_at_time) + { + have_parent = true; + break; + } + } + } + + /* The system command is orphan */ + if (!have_parent) + { + /* The system command must be deleted */ + sys_cmd_stack.cmd_list[i].command_group = NO_CMD; + sys_cmd_stack.cmd_list[i].timeout = 0.; + sys_cmd_stack.cmd_list[i].inserted_at_time = 0.; + } + } + } +} + +/** + * \brief Insert a command in a command stack. + * \param delay Delay before the execution of the command. + * \param cmd Command to execute. + * \param stack Command stack how to insert the command. + */ +static TuxDrvError +insert_command(float delay, delay_cmd_t *cmd, cmd_stack_t *stack) +{ + TuxDrvError ret = E_TUXDRV_STACKOVERFLOW; + int i; + double curtime = get_time(); + + for (i = 0; i < NRCMDS; i++) + { + if (stack->cmd_list[i].command_group == NO_CMD) + { + stack->cmd_list[i] = *cmd; + stack->cmd_list[i].timeout = delay + curtime; + stack->cmd_list[i].inserted_at_time = (float)(int)(curtime * 100) / 100.0; + ret = E_TUXDRV_NOERROR; + break; + } + } + return(ret); +} + +/** + * \brief Insert a command in the system stack. + * \param delay Delay before the execution of the command. + * \param cmd Command to execute. + */ +LIBLOCAL TuxDrvError +tux_cmd_parser_insert_sys_command(float delay, delay_cmd_t *cmd) +{ + TuxDrvError ret; + +#ifdef USE_MUTEX + mutex_lock(__stack_mutex); +#endif + + ret = insert_command(delay, cmd, &sys_cmd_stack); + +#ifdef USE_MUTEX + mutex_unlock(__stack_mutex); +#endif + return ret; +} + +/** + * \brief Insert a command in the user stack. + * \param delay Delay before the execution of the command. + * \param cmd Command to execute. + */ +LIBLOCAL TuxDrvError +tux_cmd_parser_insert_user_command(float delay, const char *cmd_str) +{ + TuxDrvError ret; + delay_cmd_t cmd; + +#ifdef USE_MUTEX + mutex_lock(__stack_mutex); +#endif + ret = parse_command(cmd_str, &cmd); + if (ret == E_TUXDRV_NOERROR) + { + ret = insert_command(delay, &cmd, &user_cmd_stack); + } + +#ifdef USE_MUTEX + mutex_unlock(__stack_mutex); +#endif + return ret; +} + +/** + * \brief Clear the delayed commands from the system stack. + * \return The result success. + */ +LIBLOCAL bool +tux_cmd_parser_clear_delay_commands(void) +{ + int i; + +#ifdef USE_MUTEX + mutex_lock(__stack_mutex); +#endif + + /* Clear user cmd */ + memset(&user_cmd_stack, 0, sizeof(cmd_stack_t)); + + /* process all pending system commands */ + for (i = 0; i < NRCMDS; i++) + { + if (sys_cmd_stack.cmd_list[i].command_group != NO_CMD) + { + execute_command(&sys_cmd_stack.cmd_list[i]); + /* no need to execute the following command as we are going + to clear the complete stack after this for loop + memset(&sys_cmd_stack.cmd_list[i], 0, sizeof(delay_cmd_t)); + */ + } + } + + /* Clear system cmd */ + memset(&sys_cmd_stack, 0, sizeof(cmd_stack_t)); + +#ifdef USE_MUTEX + mutex_unlock(__stack_mutex); +#endif + + return true; +} + +/** + * \brief Execute the expired commands from the stacks. + */ +LIBLOCAL void +tux_cmd_parser_delay_stack_perform(void) +{ + int i; + double curtime = get_time(); + +#ifdef USE_MUTEX + mutex_lock(__stack_mutex); +#endif + + for (i = 0; i < NRCMDS; i++) + { + if (user_cmd_stack.cmd_list[i].command_group != NO_CMD) + { + if (curtime >= user_cmd_stack.cmd_list[i].timeout) + { + execute_command(&user_cmd_stack.cmd_list[i]); + /* next two commands are faster than a memset + writing a null byte to the first char of cmd is sufficient + to make it an empty string + */ + user_cmd_stack.cmd_list[i].timeout = 0; + user_cmd_stack.cmd_list[i].command_group = NO_CMD; + } + } + if (sys_cmd_stack.cmd_list[i].command_group != NO_CMD) + { + if (curtime >= sys_cmd_stack.cmd_list[i].timeout) + { + execute_command(&sys_cmd_stack.cmd_list[i]); + sys_cmd_stack.cmd_list[i].timeout = 0; + sys_cmd_stack.cmd_list[i].command_group = NO_CMD; + } + } + } + +#ifdef USE_MUTEX + mutex_unlock(__stack_mutex); +#endif +} + +/** + * \brief Parse a command line string. + * \param line_str Line to parse. + * \return The error result (always E_TUXDRV_NOERROR). + */ +static TuxDrvError +parse_line(const char *line_str) +{ + float delay= 0.0; + char cmd_str[CMDSIZE] = ""; + int i; + + i = sscanf(line_str, "%f:%[^\n]", &delay, cmd_str); + + if (i == 2) + { + return tux_cmd_parser_insert_user_command(delay, cmd_str); + } + + return E_TUXDRV_NOERROR; +} + + +/** + * \brief Parse a macro string of commands. + * \param macro_str Macro string. + * \return The success result. + */ +LIBLOCAL TuxDrvError +tux_cmd_parser_parse_macro(const char *macro_str) +{ + const char lex_ret[] = "\n"; + char *line_tmp; + char macro[MACROSIZE]; + TuxDrvError ret = E_TUXDRV_NOERROR; + +#ifdef USE_MUTEX + mutex_lock(__macro_mutex); +#endif + + /* + strtok is used, this modifies the string, hence the copy + if it is ok to write 0 bytes into the argument no copy is needed + */ + strcpy(macro, macro_str); + if ((line_tmp = strtok(macro, lex_ret)) != NULL) + { + ret = parse_line(line_tmp); + if (ret != E_TUXDRV_NOERROR) + { + if (ret != E_TUXDRV_INVALIDCOMMAND) + { +#ifdef USE_MUTEX + mutex_unlock(__macro_mutex); +#endif + return(ret); + } + } + + while ((line_tmp = strtok(NULL, lex_ret)) != NULL) + { + ret = parse_line(line_tmp); + if (ret != E_TUXDRV_NOERROR) + { + if (ret != E_TUXDRV_INVALIDCOMMAND) + { +#ifdef USE_MUTEX + mutex_unlock(__macro_mutex); +#endif + return(ret); + } + } + } + } + +#ifdef USE_MUTEX + mutex_unlock(__macro_mutex); +#endif + + return ret; +} + +/** + * \brief Parse a macro file of commands. + * \param file_path Macro file path. + * \return The success result. + */ +LIBLOCAL TuxDrvError +tux_cmd_parser_parse_file(const char *file_path) +{ + char line[CMDSIZE] = ""; + FILE *macro_file; + TuxDrvError ret = E_TUXDRV_NOERROR; + +#ifdef USE_MUTEX + mutex_lock(__macro_mutex); +#endif + macro_file = fopen(file_path, "r"); + + if (macro_file) + { + while (fgets(line, sizeof(line)-2, macro_file) != NULL) + { + ret = parse_line(line); + if (ret != E_TUXDRV_NOERROR) + { + if (ret != E_TUXDRV_INVALIDCOMMAND) + { +#ifdef USE_MUTEX + mutex_unlock(__macro_mutex); +#endif + return(ret); + } + } + } + fclose(macro_file); + } + else + { + ret = E_TUXDRV_FILEERROR; + } + +#ifdef USE_MUTEX + mutex_unlock(__macro_mutex); +#endif + + return ret; +} + +/** + * \brief Parse a command string. + * \param cmd_str Command string. + * \return The result success. + */ +LIBLOCAL TuxDrvError +tux_cmd_parser_parse_command(const char *cmd_str) +{ + TuxDrvError ret; + delay_cmd_t cmd; + + /* If the parser is not enabled then fail */ + if (!cmd_parser_enable) + { + return E_TUXDRV_PARSERISDISABLED; + } + ret = parse_command(cmd_str, &cmd); + if (ret == E_TUXDRV_NOERROR) + { + execute_command(&cmd); + } + return ret; +} diff --git a/src/tux_cmd_parser.h b/src/tux_cmd_parser.h new file mode 100644 index 0000000..e5f941d --- /dev/null +++ b/src/tux_cmd_parser.h @@ -0,0 +1,61 @@ +/* + * Tux Droid - Command parser + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_cmd_parser.h + * \brief Command parser header. + * \author remi.jocaille@c2me.be + * \ingroup command_parser + */ + +#ifndef _TUX_CMD_PARSER_H_ +#define _TUX_CMD_PARSER_H_ + +#include + +#include "tux_error.h" +#include "tux_types.h" + +/** \brief Maximal size of a token */ +#define TOKENSIZE 1024 +/** \brief Maximal tokens count in a command */ +#define MAXNRTOKENS 256 + +/** \brief Token string */ +typedef char token_str_t[TOKENSIZE]; +/** \brief Token string array */ +typedef token_str_t tokens_t[MAXNRTOKENS]; + +extern void tux_cmd_parser_init(void); +extern void tux_cmd_parser_set_enable(bool value); +extern int tux_cmd_parser_get_tokens(const char *src_str, tokens_t *toks, + int max_tokens, const char *delimiters); +extern TuxDrvError tux_cmd_parser_parse_command(const char *cmd_str); +extern bool tux_cmd_parser_clear_delay_commands(void); +extern TuxDrvError tux_cmd_parser_insert_sys_command(float delay, + delay_cmd_t *cmd); +extern TuxDrvError tux_cmd_parser_insert_user_command(float delay, + const char *cmd_str); +extern void tux_cmd_parser_clean_sys_command(tux_command_t command); +extern void tux_cmd_parser_delay_stack_perform(void); +extern TuxDrvError tux_cmd_parser_parse_macro(const char *macro_str); +extern TuxDrvError tux_cmd_parser_parse_file(const char *file_path); + +#endif /* _TUX_CMD_PARSER_H_ */ diff --git a/src/tux_descriptor.c b/src/tux_descriptor.c new file mode 100644 index 0000000..d113c99 --- /dev/null +++ b/src/tux_descriptor.c @@ -0,0 +1,66 @@ +/* + * Tux Droid - Descriptor + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_descriptor.c + * \brief Descriptor functions. + * \author remi.jocaille@c2me.be + * \ingroup descriptor + */ + +#include +#include +#include + +#include "tux_descriptor.h" +#include "tux_usb.h" +#include "tux_sw_status.h" + +/** + * \brief Initialize the descriptor. + */ +LIBLOCAL void +tux_descriptor_init(void) +{ + tux_firmware_init_descriptor(); + tux_sound_flash_init_descriptor(); + tux_id_init_descriptor(); +} + +/** + * \brief Command to start the descriptor retrieving (asynchronous) + */ +LIBLOCAL void +tux_descriptor_get(void) +{ + tux_sw_status_set_intvalue(SW_ID_DESCRIPTOR_COMPLETE, false, false); + tux_id_get_descriptor(); + tux_firmware_get_descriptor(); + tux_sound_flash_get_descripor(); +} + +/** + * \brief Command to call when the descriptor was completly retrieved. + */ +LIBLOCAL void +tux_descriptor_update(void) +{ + tux_sw_status_set_intvalue(SW_ID_DESCRIPTOR_COMPLETE, true, false); +} diff --git a/src/tux_descriptor.h b/src/tux_descriptor.h new file mode 100644 index 0000000..811b6e4 --- /dev/null +++ b/src/tux_descriptor.h @@ -0,0 +1,67 @@ +/* + * Tux Droid - Status descriptor + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_descriptor.h + * \brief Descriptor header. + * \author remi.jocaille@c2me.be + * \ingroup descriptor + */ + +#ifndef _TUX_DESCRIPTOR_H_ +#define _TUX_DESCRIPTOR_H_ + +#include "tux_firmware.h" +#include "tux_sound_flash.h" +#include "tux_id.h" + +/** \brief Output file path of the descriptor */ +#define DESCRIPTOR_FILE_PATH "./descriptor.txt" + +/** + * \brief Tuxdroid descriptor. + * The descriptor contains some informations about the Tuxdroid hardware. + */ +typedef struct { + struct firmwares_t { + firmware_descriptor_t *package; /**< Package version */ + firmware_descriptor_t *tuxcore; /**< Tuxcore version */ + firmware_descriptor_t *tuxaudio; /**< Tuxaudio version */ + firmware_descriptor_t *tuxrf; /**< Tuxrf version */ + firmware_descriptor_t *fuxrf; /**< Fuxrf version */ + firmware_descriptor_t *fuxusb; /**< Fuxusb version */ + } firmwares; /**< About firmwares */ + struct driver_version_t { + unsigned int version_major; /**< Major version number */ + unsigned int version_minor; /**< Minor version number */ + unsigned int version_update; /**< Update version number */ + unsigned int version_build; /**< Build version number */ + char version_state[100]; /**< Version state */ + char version_string[100]; /**< Complete version string */ + } driver; /**< About libtuxdriver */ + sound_flash_descriptor_t *sound_flash; /**< About the sound flash */ + id_descriptor_t *id; /**< About Id connection */ +} tux_descriptor_t; + +extern void tux_descriptor_init(void); +extern void tux_descriptor_get(void); +extern void tux_descriptor_update(void); + +#endif /* _TUX_STATUS_DESCRIPTOR_H_ */ diff --git a/src/tux_driver.c b/src/tux_driver.c new file mode 100644 index 0000000..2a14d16 --- /dev/null +++ b/src/tux_driver.c @@ -0,0 +1,568 @@ +/* + * Tux Droid - Driver + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include + +#include "log.h" +#include "tux_battery.h" +#include "tux_cmd_parser.h" +#include "tux_descriptor.h" +#include "tux_error.h" +#include "tux_eyes.h" +#include "tux_firmware.h" +#include "tux_hw_status.h" +#include "tux_id.h" +#include "tux_leds.h" +#include "tux_light.h" +#include "tux_mouth.h" +#include "tux_pong.h" +#include "tux_sound_flash.h" +#include "tux_sw_status.h" +#include "tux_user_inputs.h" +#include "tux_spinning.h" +#include "tux_usb.h" +#include "tux_flippers.h" +#include "tux_types.h" +#include "version.h" + +static bool driver_started = false; +static simple_callback_t end_cycle_funct; +static simple_callback_t dongle_connected_funct; +static simple_callback_t dongle_disconnected_funct; + +static void on_frame(const unsigned char *data); +static void on_rf_state(unsigned char state); +static void on_usb_connect(void); +static void on_usb_disconnect(void); +static void on_read_loop_cycle_complete(void); + +void TuxDrv_ResetPositions(void); + +/** + * 31/08/2012 - Joël Maatteotti + */ +LIBEXPORT bool TuxDrv_Eyes_cmd_off() +{ + return tux_eyes_cmd_off(); +} + +/** + * 31/08/2012 - Joël Maatteotti + */ +LIBEXPORT bool TuxDrv_Mouth_cmd_off() +{ + return tux_mouth_cmd_off(); +} + +/** + * 31/08/2012 - Joël Maatteotti + */ +LIBEXPORT bool TuxDrv_Spinning_cmd_off() +{ + return tux_spinning_cmd_off(); +} + +/** + * 31/08/2012 - Joël Maatteotti + */ +LIBEXPORT bool TuxDrv_Flippers_cmd_off() +{ + return tux_flippers_cmd_off(); +} + +/** + * 31/08/2012 - Joël Maatteotti + */ +LIBEXPORT char *TuxDrv_SoundFlash_dump_descriptor(char *p) +{ + return tux_sound_flash_dump_descriptor(p); +} + +/** + * 31/08/2012 - Joël Maatteotti + */ +LIBEXPORT void TuxDrv_LightLevel_update() +{ + tux_light_update_level(); +} + + +/** + * + */ +LIBEXPORT void +TuxDrv_SetStatusCallback(event_callback_t funct) +{ + tux_sw_status_set_event_callback(funct); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_SetEndCycleCallback(simple_callback_t funct) +{ + end_cycle_funct = funct; +} + +/** + * + */ +LIBEXPORT void +TuxDrv_SetDongleConnectedCallback(simple_callback_t funct) +{ + dongle_connected_funct = funct; +} + +/** + * + */ +LIBEXPORT void +TuxDrv_SetDongleDisconnectedCallback(simple_callback_t funct) +{ + dongle_disconnected_funct = funct; +} + +/** + * Callback function on frame receiving. + * @param data 4 bytes array of status. + */ +static void +on_frame(const unsigned char *data) +{ + unsigned char header; + int ret; + + header = data[0]; + ret = tux_hw_status_parse_frame(data); + + if (ret == -1) + { + log_warning("STATUS FRAME : %.2x %.2x %.2x %.2x", + data[0], + data[1], + data[2], + data[3]); + } + + switch (header) + { + /* Descriptor frames */ + case FRAME_HEADER_VERSION: + tux_firmware_update_version(); + break; + case FRAME_HEADER_REVISION: + tux_firmware_update_revision(); + break; + case FRAME_HEADER_AUTHOR: + tux_firmware_update_author(); + break; + case FRAME_HEADER_SOUND_VAR: + tux_sound_flash_update(); + break; + /* Status frames */ + case FRAME_HEADER_PORTS: + if (header == ret) + { + tux_spinning_update_direction(); + tux_mouth_update_position(); + tux_eyes_update_position(); + } + break; + case FRAME_HEADER_POSITION1: + if (header == ret) + { + tux_flippers_update_movements_remaining(); + tux_mouth_update_movements_remaining(); + tux_eyes_update_movements_remaining(); + } + break; + case FRAME_HEADER_POSITION2: + if (header == ret) + { + tux_flippers_update_position(); + tux_spinning_update_movements_remaining(); + tux_eyes_update_motor(); + tux_mouth_update_motor(); + tux_flippers_update_motor(); + tux_spinning_update_left_motor(); + tux_spinning_update_right_motor(); + } + break; + case FRAME_HEADER_SENSORS1: + if (header == ret) + { + tux_sound_flash_update_general_play(); + tux_user_inputs_update_head_button(); + tux_user_inputs_update_left_wing_button(); + tux_user_inputs_update_right_wing_button(); + } + break; + case FRAME_HEADER_ID: + tux_id_update_number(); + break; + case FRAME_HEADER_IR: + tux_user_inputs_init_time_RC5(); + break; + case FRAME_HEADER_BATTERY: + tux_user_inputs_update_charger_state(); + if (header == ret) + { + tux_battery_update_level(); + } + break; + case FRAME_HEADER_LIGHT: + if (header == ret) + { + tux_light_update_level(); + } + break; + case FRAME_HEADER_LED: + if (header == ret) + { + tux_leds_update_left(); + tux_leds_update_right(); + } + break; + /* Don't work ... + case FRAME_HEADER_FLASH_PROG: + if (header == ret) + { + tux_sound_flash_update_prog_current_track(); + tux_sound_flash_update_prog_last_track_size(); + } + break; + */ + case FRAME_HEADER_AUDIO: + tux_sound_flash_update_flash_play(); + break; + /*case FRAME_HEADER_PONG: + tux_pong_update(); + break;*/ + } +} + +/** + * Callback function on radio state changed. + * @param state state of the radio connection. + */ +static void +on_rf_state(unsigned char state) +{ + tux_sw_status_set_intvalue(SW_ID_RF_STATE, state, true); + if (state) + { + tux_descriptor_get(); + /* For reinitilizing the remote control task */ + tux_user_inputs_init(); + /* Reset the body state */ + TuxDrv_ResetPositions(); + } +} + +/** + * Callback function on fux dongle plugging. + */ +static void +on_usb_connect(void) +{ + data_frame wakeup_frame = {0xB6, 0xFF, 0x01, 0x00}; + + tux_descriptor_init(); + tux_hw_status_init(); + tux_sw_status_init(); + tux_user_inputs_init(); + tux_cmd_parser_init(); + tux_sw_status_set_intvalue(SW_ID_DONGLE_PLUG, true, true); + /* Waking up Tux Droid */ + tux_usb_send_to_tux(wakeup_frame); + if (dongle_connected_funct) + { + dongle_connected_funct(); + } + /* If default windows sound card is Tuxdroid-TTS then set + * the default card to TuxDroid-Audio */ +#ifdef WIN32 + tux_sound_flash_avoid_tts_default_sound_card(); +#endif +} + +/** + * Callback function on fux dongle unplugging. + */ +static void +on_usb_disconnect(void) +{ + tux_sw_status_set_intvalue(SW_ID_RF_STATE, false, true); + tux_sw_status_set_intvalue(SW_ID_DONGLE_PLUG, false, true); + if (dongle_disconnected_funct) + { + dongle_disconnected_funct(); + } +} + +/** + * Callback function on end of a cycle of usb read. + */ +static void +on_read_loop_cycle_complete(void) +{ + tux_user_inputs_update_RC5(); + /* tux_pong_get(); */ + /* tux_firmware_state_machine_call(); */ + tux_sound_flash_state_machine_call(); + tux_hw_status_header_counter_check(); + + if (end_cycle_funct) + { + end_cycle_funct(); + } + + tux_cmd_parser_delay_stack_perform(); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_PerformCommand(double delay, const char *cmd_str) +{ + if (delay == 0.0) + { + log_debug("Perform an instant command : [%s]", cmd_str); + return tux_cmd_parser_parse_command(cmd_str); + } + else + { + log_debug("Perform a delayed command : [%s], %f", cmd_str, delay); + return tux_cmd_parser_insert_user_command(delay, cmd_str); + } +} + +/** + * + */ +LIBEXPORT void +TuxDrv_ClearCommandStack(void) +{ + tux_cmd_parser_clear_delay_commands(); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_PerformMacroFile(const char *file_path) +{ + return tux_cmd_parser_parse_file(file_path); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_PerformMacroText(const char *macro) +{ + return tux_cmd_parser_parse_macro(macro); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_SoundReflash(const char *tracks) +{ + return tux_sound_flash_cmd_reflash(tracks); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_SetLogLevel(log_level_t level) +{ + log_set_level(level); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_SetLogTarget(log_target_t target) +{ + log_open(target); +} + +/** + * + */ +LIBEXPORT int +TuxDrv_TokenizeStatus(char *status, tokens_t *tokens) +{ + return tux_cmd_parser_get_tokens(status, tokens, 32, ":,"); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_GetStatusName(int id, char *name) +{ + return tux_sw_status_name_from_id(id, name); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_GetStatusId(const char *name, int *id) +{ + return tux_sw_status_id_from_name(name, id); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_GetStatusState(int id, char *state) +{ + return tux_sw_status_get_state_str(id, state); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_GetAllStatusState(char *state) +{ + tux_sw_status_get_all_state_str(state); +} + +/** + * + */ +LIBEXPORT TuxDrvError +TuxDrv_GetStatusValue(int id, char *value) +{ + return tux_sw_status_get_value_str(id, value); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_ResetDongle(void) +{ + tux_usb_reset(); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_ResetPositions(void) +{ + TuxDrv_PerformCommand(0.0, "TUX_CMD:EYES:OPEN"); + TuxDrv_PerformCommand(0.3, "TUX_CMD:MOUTH:CLOSE"); + TuxDrv_PerformCommand(0.0, "TUX_CMD:FLIPPERS:DOWN"); + TuxDrv_PerformCommand(0.0, "TUX_CMD:SPINNING:OFF"); + TuxDrv_PerformCommand(0.0, "TUX_CMD:LED:ON:LED_BOTH,1.0"); +} + +/** + * + */ +LIBEXPORT void +TuxDrv_GetDescriptor(tux_descriptor_t *tux_desc) +{ + tux_desc->firmwares.package = &firmware_release_desc; + tux_desc->firmwares.tuxcore = &firmwares_desc[TUXCORE_CPU_NUM]; + tux_desc->firmwares.tuxaudio = &firmwares_desc[TUXAUDIO_CPU_NUM]; + tux_desc->firmwares.tuxrf = &firmwares_desc[TUXRF_CPU_NUM]; + tux_desc->firmwares.fuxrf = &firmwares_desc[FUXRF_CPU_NUM]; + tux_desc->firmwares.fuxusb = &firmwares_desc[FUXUSB_CPU_NUM]; + tux_desc->sound_flash = &sound_flash_desc; + tux_desc->id = &id_desc; + tux_desc->driver.version_major = VER_MAJOR; + tux_desc->driver.version_minor = VER_MINOR; + tux_desc->driver.version_update = VER_UPDATE; + tux_desc->driver.version_build = VER_REVISION; + strcpy(tux_desc->driver.version_state, VER_STATE); + sprintf(tux_desc->driver.version_string, + "libtuxdriver_%d.%d.%d-r%d", + VER_MAJOR, + VER_MINOR, + VER_UPDATE, + VER_REVISION); +} + +/** + * Get the description of an error code. + */ +LIBEXPORT const char * +TuxDrv_StrError(TuxDrvError error_code) +{ + return tux_error_strerror(error_code); +} + +/** + * Start tux driver. + */ +LIBEXPORT void +TuxDrv_Start(void) +{ + printf("libtuxdriver_%d.%d.%d-r%d\n\n", + VER_MAJOR, + VER_MINOR, + VER_UPDATE, + VER_REVISION); + + tux_usb_init_module(); + tux_usb_set_frame_callback(on_frame); + tux_usb_set_rf_state_callback(on_rf_state); + tux_usb_set_connect_dongle_callback(on_usb_connect); + tux_usb_set_disconnect_dongle_callback(on_usb_disconnect); + tux_usb_set_loop_cycle_complete_callback(on_read_loop_cycle_complete); + tux_descriptor_init(); + tux_hw_status_init(); + tux_sw_status_init(); + tux_user_inputs_init(); + tux_cmd_parser_init(); + driver_started = true; + + while (driver_started) + { + tux_usb_start(); + sleep(1); + } + + tux_usb_exit_module(); +} + +/** + * Stop tux driver. + */ +LIBEXPORT void +TuxDrv_Stop(void) +{ + driver_started = false; + tux_usb_stop(); +} diff --git a/src/tux_error.c b/src/tux_error.c new file mode 100644 index 0000000..7d25249 --- /dev/null +++ b/src/tux_error.c @@ -0,0 +1,65 @@ +/* + * Tux Droid - Error + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_error.c + * \brief Error functions. + * \author remi.jocaille@c2me.be + * \ingroup error + */ + +#include "tux_misc.h" +#include "tux_error.h" + +/** + * \brief Get an explicite message from an error code. + * \param error_code Error code. + * \return The explicite message. + */ +LIBLOCAL const char * +tux_error_strerror(TuxDrvError error_code) +{ + switch (error_code) { + case E_TUXDRV_NOERROR: + return "No error"; + case E_TUXDRV_PARSERISDISABLED: + return "The parser of command is disabled"; + case E_TUXDRV_INVALIDCOMMAND: + return "Invalid command"; + case E_TUXDRV_STACKOVERFLOW: + return "The stack of commands is full"; + case E_TUXDRV_FILEERROR: + return "File error"; + case E_TUXDRV_BADWAVFILE: + return "Wave file error"; + case E_TUXDRV_INVALIDIDENTIFIER: + return "The identifier is unknow"; + case E_TUXDRV_INVALIDNAME: + return "The name is unknow"; + case E_TUXDRV_INVALIDPARAMETER: + return "One or more parameters are invalid"; + case E_TUXDRV_BUSY: + return "The system is busy"; + case E_TUXDRV_WAVSIZEEXCEDED: + return "The size of the selection exceeds 127 blocks"; + default: + return "Unknow error"; + } +} diff --git a/src/tux_error.h b/src/tux_error.h new file mode 100644 index 0000000..53157a8 --- /dev/null +++ b/src/tux_error.h @@ -0,0 +1,55 @@ +/* + * Tux Droid - Error + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_error.h + * \brief Error header. + * \author remi.jocaille@c2me.be + * \ingroup error + */ + +#ifndef _TUX_ERROR_H_ +#define _TUX_ERROR_H_ + +/** \brief Errors code begin index */ +#define TUX_ERROR_BEGIN 256 + +/** \brief TuxDrvError type */ +typedef int TuxDrvError; +/** \brief Error codes enumeration */ +typedef enum +{ + E_TUXDRV_NOERROR = 0, /**< No error */ + E_TUXDRV_PARSERISDISABLED = TUX_ERROR_BEGIN, /**< The parser is disabled*/ + E_TUXDRV_INVALIDCOMMAND, /**< The command is invalid */ + E_TUXDRV_STACKOVERFLOW, /**< Command stack overflow */ + E_TUXDRV_FILEERROR, /**< File IO error */ + E_TUXDRV_BADWAVFILE, /**< Bad wave file */ + E_TUXDRV_INVALIDIDENTIFIER, /**< Invalid status identifier */ + E_TUXDRV_INVALIDNAME, /**< Invalid status name */ + E_TUXDRV_INVALIDPARAMETER, /**< Invalid command parameter */ + E_TUXDRV_BUSY, /**< Tuxdriver is busy */ + E_TUXDRV_WAVSIZEEXCEDED, /**< Wave size exceded (for sound flash) */ +} tux_drv_error_t; + +extern const char *tux_error_strerror(TuxDrvError error_code); + +#endif /* _TUX_ERROR_H_ */ + diff --git a/src/tux_eyes.c b/src/tux_eyes.c new file mode 100644 index 0000000..a428747 --- /dev/null +++ b/src/tux_eyes.c @@ -0,0 +1,192 @@ +/* + * Tux Droid - Eyes + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_eyes.c + * \brief Eyes functions. + * \author remi.jocaille@c2me.be + * \ingroup eyes + */ + +#include + +#include "tux_cmd_parser.h" +#include "tux_eyes.h" +#include "tux_hw_cmd.h" +#include "tux_hw_status.h" +#include "tux_misc.h" +#include "tux_movements.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" + +/** Counter of eyes movements */ +static unsigned char mvmt_counter = 0; + +/** + * \brief Update the status of the position of the eyes. + */ +LIBLOCAL void +tux_eyes_update_position(void) +{ + char *new_position = ""; + + if (!hw_status_table.ports.portd.bits.eyes_open_switch) + { + new_position = STRING_VALUE_OPEN; + } + else + { + if (!hw_status_table.ports.portd.bits.eyes_closed_switch) + { + new_position = STRING_VALUE_CLOSE; + } + else + { + new_position = STRING_VALUE_NDEF; + } + } + + tux_sw_status_set_strvalue(SW_ID_EYES_POSITION, new_position, true); +} + +/** + * \brief Update the status of the motor state of the eyes. + */ +LIBLOCAL void +tux_eyes_update_motor(void) +{ + unsigned char new_state; + + new_state = hw_status_table.position2.motors.bits.eyes_on; + tux_sw_status_set_intvalue(SW_ID_EYES_MOTOR_ON, new_state, true); +} + +/** + * \brief Update the status of the eyes movements remaining. + */ +LIBLOCAL void +tux_eyes_update_movements_remaining(void) +{ + unsigned char new_count; + + new_count = hw_status_table.position1.eyes_remaining_mvm; + + mvmt_counter = new_count; + tux_sw_status_set_intvalue(SW_ID_EYES_REMAINING_MVM, new_count, true); +} + +/** + * \brief Execute an eyes on command. + * \param counter Number of eyes movements. + * \param final_state Desired final state of the eyes. + * \return The command success result. + */ +LIBLOCAL bool +tux_eyes_cmd_on(unsigned char counter, unsigned char final_state) +{ + return tux_movement_cmd_on(MOVE_EYES, counter, final_state); +} + +/** + * \brief Execute an eyes on command. + * \param timeout Duration of the movement. + * \param final_state Desired final state of the eyes. + * \return The command success result. + */ +LIBLOCAL bool +tux_eyes_cmd_on_during(float timeout, unsigned char final_state) +{ + bool ret; + data_frame frame = {EYES_BLINK_CMD, 0, 0, 0}; + delay_cmd_t cmd = { 0.0, TUX_CMD, EYES }; + + /* Short movements */ + if (timeout < 0.3) + { + return tux_movement_perform(MOVE_EYES, 0, timeout, 5, final_state, + false); + } + + /* Long movements */ + ret = tux_usb_send_to_tux(frame); + if (!ret) + { + return false; + } + + mvmt_counter = 255; + tux_sw_status_set_intvalue(SW_ID_EYES_REMAINING_MVM, mvmt_counter, true); + + switch (final_state) { + case FINAL_ST_UNDEFINED: + cmd.sub_command = OFF; + break; + case FINAL_ST_OPEN_UP: + cmd.sub_command = OPEN; + break; + case FINAL_ST_CLOSE_DOWN: + cmd.sub_command = CLOSE; + break; + case FINAL_ST_STOP: + cmd.sub_command = OFF; + break; + } + ret = tux_cmd_parser_insert_sys_command(timeout, &cmd); + + return ret; +} + +/** + * \brief Open the eyes. + * \return The command success result. + */ +LIBLOCAL bool +tux_eyes_cmd_open(void) +{ + return tux_movement_perform(MOVE_EYES, 0, 0, 5, FINAL_ST_OPEN_UP, false); +} + +/** + * \brief Close the eyes. + * \return The command success result. + */ +LIBLOCAL bool +tux_eyes_cmd_close(void) +{ + return tux_movement_perform(MOVE_EYES, 0, 0, 5, FINAL_ST_CLOSE_DOWN, false); +} + +/** + * \brief Stop the eyes movement. + * \return The command success result. + */ +LIBLOCAL bool +tux_eyes_cmd_off(void) +{ + bool ret; + + tux_cmd_parser_clean_sys_command(EYES); + ret = tux_movement_perform(MOVE_EYES, 0, 0, 5, FINAL_ST_STOP, false); + mvmt_counter = 0; + tux_sw_status_set_intvalue(SW_ID_EYES_REMAINING_MVM, mvmt_counter, true); + + return ret; +} diff --git a/src/tux_eyes.h b/src/tux_eyes.h new file mode 100644 index 0000000..b8d9a87 --- /dev/null +++ b/src/tux_eyes.h @@ -0,0 +1,47 @@ +/* + * Tux Droid - Eyes + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_eyes.h + * \brief Eyes header. + * \author remi.jocaille@c2me.be + * \ingroup eyes + */ + +#ifndef _TUX_EYES_H_ +#define _TUX_EYES_H_ + +/** \brief Eyes position not defined */ +#define TUX_EYES_POSITION_NDEF 0 +/** \brief Eyes position open */ +#define TUX_EYES_POSITION_OPEN 1 +/** \brief Eyes position close */ +#define TUX_EYES_POSITION_CLOSE 2 + +extern void tux_eyes_update_position(void); +extern void tux_eyes_update_motor(void); +extern void tux_eyes_update_movements_remaining(void); +extern bool tux_eyes_cmd_on(unsigned char counter, unsigned char final_state); +extern bool tux_eyes_cmd_on_during(float timeout, unsigned char final_state); +extern bool tux_eyes_cmd_open(void); +extern bool tux_eyes_cmd_close(void); +extern bool tux_eyes_cmd_off(void); + +#endif /* _TUX_EYES_H_ */ diff --git a/src/tux_firmware.c b/src/tux_firmware.c new file mode 100644 index 0000000..249ef31 --- /dev/null +++ b/src/tux_firmware.c @@ -0,0 +1,650 @@ +/* + * Tux Droid - Firmware + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_firmware.c + * \brief Firmware functions. + * \author remi.jocaille@c2me.be + * \ingroup firmware + * \todo Remove deprecated functions. + * \todo Implement the new way to retrieve the RF versions. + */ + +#include +#include + +#include "tux_descriptor.h" +#include "tux_firmware.h" +#include "tux_hw_status.h" +#include "tux_misc.h" +#include "tux_sw_status.h" +#include "tux_usb.h" +#include "log.h" + +/** State of the versioning state machine. */ +static versioning_state_t versioning_state = STDBY; +/** Used to hold the CPU number that is currently sending its information as it + * needs multiple commands to send it all. */ +static int current_cpu = INVALID_CPU_NUM; +/** Number of cpu */ +static int cpu_num; +/** Retries counter of firmware versions */ +static int retries; +static int global_retries; + +/** Descriptor of the firmwares */ +LIBLOCAL firmwares_descriptor_t firmwares_desc; +/** Descriptor of the firmwares package */ +LIBLOCAL firmware_descriptor_t firmware_release_desc; + +LIBLOCAL char knowed_tuxcore_symbolic_version[256] = "Tuxcore 0.0.0"; +LIBLOCAL char knowed_tuxaudio_symbolic_version[256] = "Tuxaudio 0.0.0"; +LIBLOCAL char knowed_fuxusb_symbolic_version[256] = "FuxUSB 0.0.0"; +LIBLOCAL char knowed_fuxrf_symbolic_version[256] = "FuxRF 0.0.0"; +LIBLOCAL char knowed_tuxrf_symbolic_version[256] = "TuxRF 0.0.0"; + +/** + * \brief Get the name of a tux cpu. + * \param id cpu identifier. + * \return a string. + */ +static const char * +cpu_id_to_name(int id) +{ + switch (id) + { + case TUXCORE_CPU_NUM: + return CPU_STR_NAME_TUXCORE; + case TUXAUDIO_CPU_NUM: + return CPU_STR_NAME_TUXAUDIO; + case TUXRF_CPU_NUM: + return CPU_STR_NAME_TUXRF; + case FUXRF_CPU_NUM: + return CPU_STR_NAME_FUXRF; + case FUXUSB_CPU_NUM: + return CPU_STR_NAME_FUXUSB; + default: + return "CPU_UNKNOW"; + } +} + +/** + * \deprecated + */ +static void +load_knowed_symbolic_versions(void) +{ +#ifdef LOCK_TUX + FILE *f; + char ret_str[] = "\n"; + char *ret_c; + + f = fopen("./symbolic_versions", "r"); + + if (f) { + fgets(knowed_tuxcore_symbolic_version, + sizeof(knowed_tuxcore_symbolic_version)-2, f); + ret_c = strstr(knowed_tuxcore_symbolic_version, ret_str); + *ret_c = '\0'; + fgets(knowed_tuxaudio_symbolic_version, + sizeof(knowed_tuxaudio_symbolic_version)-2, f); + ret_c = strstr(knowed_tuxaudio_symbolic_version, ret_str); + *ret_c = '\0'; + fgets(knowed_fuxusb_symbolic_version, + sizeof(knowed_fuxusb_symbolic_version)-2, f); + ret_c = strstr(knowed_fuxusb_symbolic_version, ret_str); + *ret_c = '\0'; + fgets(knowed_fuxrf_symbolic_version, + sizeof(knowed_fuxrf_symbolic_version)-2, f); + ret_c = strstr(knowed_fuxrf_symbolic_version, ret_str); + *ret_c = '\0'; + fgets(knowed_tuxrf_symbolic_version, + sizeof(knowed_tuxrf_symbolic_version)-2, f); + ret_c = strstr(knowed_tuxrf_symbolic_version, ret_str); + *ret_c = '\0'; + fclose(f); + } +#endif +} + +/** + * \deprecated + */ +#ifdef LOCK_TUX +static void +save_knowed_symbolic_versions(void) +{ + FILE *f; + + f = fopen("./symbolic_versions", "w"); + + if (f) + { + fprintf(f, "%s\n", firmwares_desc[TUXCORE_CPU_NUM].version_string); + fprintf(f, "%s\n", firmwares_desc[TUXAUDIO_CPU_NUM].version_string); + fprintf(f, "%s\n", firmwares_desc[FUXUSB_CPU_NUM].version_string); + fprintf(f, "%s\n", firmwares_desc[FUXRF_CPU_NUM].version_string); + fprintf(f, "%s\n", firmwares_desc[TUXRF_CPU_NUM].version_string); + fclose(f); + } +} +#endif + +/** + * \deprecated + */ +LIBLOCAL bool +tux_firmware_check_new_descriptor(bool save) +{ +#ifdef LOCK_TUX + bool ret = false; + + if (strcmp(knowed_tuxcore_symbolic_version, + firmwares_desc[TUXCORE_CPU_NUM].version_string)) + { + ret = true; + } + if (strcmp(knowed_tuxaudio_symbolic_version, + firmwares_desc[TUXAUDIO_CPU_NUM].version_string)) + { + ret = true; + } + if (strcmp(knowed_tuxrf_symbolic_version, + firmwares_desc[TUXRF_CPU_NUM].version_string)) + { + ret = true; + } + + if (save) + { + save_knowed_symbolic_versions(); + } + + return ret; +#else + return false; +#endif +} + +/** + * \brief Initilize the firmware part of the descriptor. + */ +LIBLOCAL void +tux_firmware_init_descriptor(void) +{ + memset(&firmwares_desc, 0, sizeof(firmwares_descriptor_t)); + memset(&firmware_release_desc, 0, sizeof(firmware_descriptor_t)); + load_knowed_symbolic_versions(); + versioning_state = STDBY; + global_retries = TUX_FIRMWARE_GLOBAL_RETRY_COUNT; +} + +/** + * \deprecated + */ +static char * +dump_descriptor_of_cpu(char *descriptor, const firmware_descriptor_t *desc) +{ + char *p = descriptor + strlen(descriptor); + + p = p + sprintf(p, + " CPU id : \t\t\t%d\n" + " CPU name : \t\t\t%s\n" + " Version major : \t\t%d\n" + " Version minor : \t\t%d\n" + " Version update : \t\t%d\n" + " Revision : \t\t\t%d\n" + " Released : \t\t\t%s\n" + " Local modification : \t%s\n" + " Mixed revisions : \t\t%s\n" + " Author id : \t\t%d\n" + " Variation : \t\t%d\n" + " Symbolic version : \t\t[%s]\n", + desc->cpu_id, + cpu_id_to_name(desc->cpu_id), + desc->version_major, + desc->version_minor, + desc->version_update, + desc->revision, + desc->release ? "True" : "False", + desc->local_modification ? "True" : "False", + desc->mixed_revisions ? "True" : "False", + desc->author, + desc->variation, + desc->version_string + ); + return p; +} + + + +/** + * \brief Update the status of the version of a cpu. + */ +LIBLOCAL void +tux_firmware_update_version(void) +{ + firmware_descriptor_t *f_desc; + + current_cpu = hw_status_table.version.cm.bits.cpu_number; + f_desc = &firmwares_desc[current_cpu]; + f_desc->cpu_id = current_cpu; + f_desc->version_major = hw_status_table.version.cm.bits.major; + f_desc->version_minor = hw_status_table.version.minor; + f_desc->version_update = hw_status_table.version.update; + + if ((current_cpu == TUXRF_CPU_NUM) || (current_cpu == FUXRF_CPU_NUM)) + { + sprintf(f_desc->version_string, "%s_%d.%d.%d", + cpu_id_to_name(current_cpu), + f_desc->version_major, + f_desc->version_minor, + f_desc->version_update); + } +} + +/** + * \brief Update the status of the revision of a cpu. + */ +LIBLOCAL void +tux_firmware_update_revision(void) +{ + char revision_string[40]; + firmware_descriptor_t *f_desc; + + if (current_cpu == INVALID_CPU_NUM) + { + return; + } + + f_desc = &firmwares_desc[current_cpu]; + f_desc->revision = hw_status_table.revision.msb_number << 8; + f_desc->revision += hw_status_table.revision.lsb_number; + f_desc->release = hw_status_table.revision.release_type.bits.original_release; + f_desc->local_modification = hw_status_table.revision.release_type.bits.local_modification; + f_desc->mixed_revisions = hw_status_table.revision.release_type.bits.mixed_update; + + if ((f_desc->local_modification || f_desc->mixed_revisions) + && f_desc->release) + { + f_desc->release = 0; + /* Log warning */ + } + + sprintf(revision_string, " - r%d (SVN/UNRELEASED)", f_desc->revision); + sprintf(f_desc->version_string, "%s_%d.%d.%d%s%s%s", + cpu_id_to_name(current_cpu), + f_desc->version_major, f_desc->version_minor, + f_desc->version_update, + f_desc->release ? "" : revision_string, + f_desc->local_modification ? "(modified locally)" : "", + f_desc->mixed_revisions ? "(mixed revisions)" : ""); +} + +/** + * \brief Update the status of the author of a cpu. + */ +LIBLOCAL void +tux_firmware_update_author(void) +{ + firmware_descriptor_t *f_desc; + + if (current_cpu == INVALID_CPU_NUM) + { + return; + } + + f_desc = &firmwares_desc[current_cpu]; + f_desc->author = hw_status_table.author.msb_id << 8; + f_desc->author += hw_status_table.author.lsb_id; + f_desc->variation = hw_status_table.author.variation_number; +} + +/** + * \brief Request the versionning of a cpu. + * \param cpu_id cpu identifier. + * \return 0 or 1 + */ +static int +send_firmware_versionning_request(const int cpu_id) +{ + int ret; + unsigned char data[4] = {(cpu_id + 2), 0, 0, 0}; + +#ifdef USB_DEBUG + printf("USB debug : Send firmware versionning request (%s)\n", + cpu_id_to_name(cpu_id)); +#endif + + if (cpu_id == FUXUSB_CPU_NUM) + { + ret = tux_usb_send_to_dongle(data); + } + else + { + ret = tux_usb_send_to_tux(data); + } + if (ret) + { + return 1; + } + else + { + return 0; + } +} + + +/** + * \brief Test the version of a firmware. + * \param firmware Firmware descriptor. + * \param major Major number to match. + * \param minor Minor number to match. + * \param update Update number to match. + * \return The firmware version match result. + */ +static bool +test_fw_ver(const firmware_descriptor_t *firmware, int major, int minor, + int update) +{ + if ((firmware->version_major != major) || + (firmware->version_minor != minor) || + (firmware->version_update != update)) + { + return false; + } + else + { + return true; + } +} + +/** + * \brief Function to determine the firmwares package version. + */ +static void +determine_release_package(void) +{ + int cpu_num = LOWEST_CPU_NUM; + int major = 0, minor = 0, update = 0; + int t_major, t_minor, t_update; + bool perfect_package = true; + bool ret; + bool first_rf_ver; + firmware_descriptor_t *release, *firmware; + +/* + firmwares_desc[TUXCORE_CPU_NUM].version_major = 0; + firmwares_desc[TUXCORE_CPU_NUM].version_minor = 4; + firmwares_desc[TUXCORE_CPU_NUM].version_update = 0; + firmwares_desc[TUXCORE_CPU_NUM].release = true; + firmwares_desc[TUXAUDIO_CPU_NUM].version_major = 0; + firmwares_desc[TUXAUDIO_CPU_NUM].version_minor = 4; + firmwares_desc[TUXAUDIO_CPU_NUM].version_update = 0; + firmwares_desc[TUXAUDIO_CPU_NUM].release = true; + firmwares_desc[TUXRF_CPU_NUM].version_major = 0; + firmwares_desc[TUXRF_CPU_NUM].version_minor = 3; + firmwares_desc[TUXRF_CPU_NUM].version_update = 0; + firmwares_desc[TUXRF_CPU_NUM].release = true; + firmwares_desc[FUXRF_CPU_NUM].version_major = 0; + firmwares_desc[FUXRF_CPU_NUM].version_minor = 3; + firmwares_desc[FUXRF_CPU_NUM].version_update = 0; + firmwares_desc[FUXRF_CPU_NUM].release = true; + firmwares_desc[FUXUSB_CPU_NUM].version_major = 0; + firmwares_desc[FUXUSB_CPU_NUM].version_minor = 4; + firmwares_desc[FUXUSB_CPU_NUM].version_update = 0; + firmwares_desc[FUXUSB_CPU_NUM].release = true; +*/ + + release = &firmware_release_desc; + + strcpy(release->version_string, UNOFFICIAL_RELEASE_STR); + + /* Get the version of the first firmware */ + t_major = firmwares_desc[LOWEST_CPU_NUM].version_major; + t_minor = firmwares_desc[LOWEST_CPU_NUM].version_minor; + t_update = firmwares_desc[LOWEST_CPU_NUM].version_update; + + /* Check if both RF versions is 0.3.0 (first revision) */ + first_rf_ver = test_fw_ver(&firmwares_desc[FUXRF_CPU_NUM], 0, 3, 0); + first_rf_ver &= test_fw_ver(&firmwares_desc[TUXRF_CPU_NUM], 0, 3, 0); + + /* Check if all firmwares is released and it have the same + * version */ + while (cpu_num <= HIGHEST_CPU_NUM) + { + if (first_rf_ver && ((cpu_num == FUXRF_CPU_NUM) || + (cpu_num == TUXRF_CPU_NUM))) + { + cpu_num ++; + } + else + { + firmware = &firmwares_desc[cpu_num]; + if (!firmware->release) + { + /* Firmware is not released -> FAIL */ + return; + } + if (!test_fw_ver(firmware, t_major, t_minor, t_update)) + { + /* Firmware have not the same version than the first one */ + perfect_package = false; + } + cpu_num ++; + } + } + + /* If the firmware set is not a perfect package */ + if (!perfect_package) + { + /* Determine the general version major */ + cpu_num = LOWEST_CPU_NUM; + while (cpu_num <= HIGHEST_CPU_NUM) + { + firmware = &firmwares_desc[cpu_num]; + if (firmware->version_major > major) + { + major = firmware->version_major; + } + cpu_num ++; + } + /* Determine the general version minor */ + cpu_num = LOWEST_CPU_NUM; + while (cpu_num <= HIGHEST_CPU_NUM) + { + firmware = &firmwares_desc[cpu_num]; + if ((firmware->version_major == major) && + (firmware->version_minor > minor)) + { + minor = firmware->version_minor; + } + cpu_num ++; + } + /* Determine the general version update */ + cpu_num = LOWEST_CPU_NUM; + while (cpu_num <= HIGHEST_CPU_NUM) + { + firmware = &firmwares_desc[cpu_num]; + if ((firmware->version_major == major) && + (firmware->version_minor == minor) && + (firmware->version_update > update)) + { + update = firmware->version_update; + } + cpu_num ++; + } + + /* Test if the firmware set is an official package */ + + /* release 0.3.1, tuxcore and tuxaudio updated to 0.3.1 */ + if ((major == 0) && (minor == 3) && (update == 1)) + { + ret = test_fw_ver(&firmwares_desc[TUXCORE_CPU_NUM], 0, 3, 1); + ret &= test_fw_ver(&firmwares_desc[TUXAUDIO_CPU_NUM], 0, 3, 1); + ret &= test_fw_ver(&firmwares_desc[FUXUSB_CPU_NUM], 0, 3, 0); + if (!ret) + { + /* Not official package -> FAIL */ + return; + } + } + else + { + /* Not other special case -> FAIL */ + return; + } + } + else + { + major = t_major; + minor = t_minor; + update = t_update; + } + + /* It's a release. */ + release->version_major = major; + release->version_minor = minor; + release->version_update = update; + /* Author and variation are taken from tuxcore as that's the CPU most */ + /* likely to be customized. */ + release->author = firmwares_desc[TUXCORE_CPU_NUM].author; + release->variation = firmwares_desc[TUXCORE_CPU_NUM].variation; + sprintf(release->version_string, "tuxdroid firmware release %d.%d.%d", + major, minor, update); + printf("%s\n", release->version_string); +} + +/** + * \brief State machine task to retrieving the firmwares versions. + */ +LIBLOCAL void +tux_firmware_state_machine_call(void) +{ + if (!tux_usb_connected()) + { + return; + } + + switch (versioning_state) { + case STDBY: + break; + case INIT: + tux_firmware_init_descriptor(); + cpu_num = LOWEST_CPU_NUM; + versioning_state = INFO_REQ; + retries = TUX_FIRMWARE_RETRY_COUNT; + break; + case INFO_REQ: + if (cpu_num > HIGHEST_CPU_NUM) + { + versioning_state = SPECIAL; + break; + } + + if (firmwares_desc[cpu_num].version_string[0] == '\0') + { + if (!retries) + { + cpu_num++; + break; + } + + if (!send_firmware_versionning_request(cpu_num)) + { + retries--; + break; + } + + } + retries = TUX_FIRMWARE_RETRY_COUNT; + versioning_state = INFO_GET; + break; + case INFO_GET: + if (!retries) + { + cpu_num++; + retries = TUX_FIRMWARE_RETRY_COUNT; + versioning_state = INFO_REQ; + break; + } + + if (firmwares_desc[cpu_num].version_string[0] != '\0') + { + cpu_num++; + retries = TUX_FIRMWARE_RETRY_COUNT; + versioning_state = INFO_REQ; + break; + } + retries--; + break; + case SPECIAL: + if ((firmwares_desc[TUXCORE_CPU_NUM].version_string[0] == '\0') || + (firmwares_desc[TUXAUDIO_CPU_NUM].version_string[0] == '\0') || + (firmwares_desc[TUXRF_CPU_NUM].version_string[0] == '\0') || + (firmwares_desc[FUXRF_CPU_NUM].version_string[0] == '\0')) + { + if (global_retries) + { + versioning_state = INIT; + global_retries--; + } + } + else + { + versioning_state = RELEASE; + } + break; + case RELEASE: + determine_release_package(); + versioning_state = FINALIZE; + break; + case FINALIZE: + tux_sw_status_set_strvalue(SW_ID_TUXCORE_SYMBOLIC_VERSION, + firmwares_desc[TUXCORE_CPU_NUM].version_string, true); + tux_sw_status_set_strvalue(SW_ID_TUXAUDIO_SYMBOLIC_VERSION, + firmwares_desc[TUXAUDIO_CPU_NUM].version_string, true); + tux_sw_status_set_strvalue(SW_ID_FUXUSB_SYMBOLIC_VERSION, + firmwares_desc[FUXUSB_CPU_NUM].version_string, true); + tux_sw_status_set_strvalue(SW_ID_FUXRF_SYMBOLIC_VERSION, + firmwares_desc[FUXRF_CPU_NUM].version_string, true); + tux_sw_status_set_strvalue(SW_ID_TUXRF_SYMBOLIC_VERSION, + firmwares_desc[TUXRF_CPU_NUM].version_string, true); + tux_descriptor_update(); + /* Check version of tuxcore and tuxaudio */ + versioning_state = STDBY; + break; + default: + break; + } +} + +/** + * \brief Start the firmware versions retrieving. + */ +LIBLOCAL void +tux_firmware_get_descriptor(void) +{ + if (versioning_state == STDBY) + { + tux_firmware_init_descriptor(); + versioning_state = INIT; + } +} diff --git a/src/tux_firmware.h b/src/tux_firmware.h new file mode 100644 index 0000000..71d9a48 --- /dev/null +++ b/src/tux_firmware.h @@ -0,0 +1,121 @@ +/* + * Tux Droid - Firmware + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_firmware.h + * \brief Firmware header. + * \author remi.jocaille@c2me.be + * \ingroup firmware + */ + +#ifndef _TUX_FIRMWARE_H_ +#define _TUX_FIRMWARE_H_ + +#include + +/** \brief First CPU index */ +#define LOWEST_CPU_NUM 0 +/** \brief Last CPU index */ +#define HIGHEST_CPU_NUM 4 +/** \brief Number of CPU */ +#define NUMBER_OF_CPU (HIGHEST_CPU_NUM + 1) +/** \brief Maximal length of a version string */ +#define VERSION_STRING_LENGTH 256 +/** \brief Tuxcore CPU name */ +#define CPU_STR_NAME_TUXCORE "Tuxcore" +/** \brief Tuxaudio CPU name */ +#define CPU_STR_NAME_TUXAUDIO "Tuxaudio" +/** \brief Tuxrf CPU name */ +#define CPU_STR_NAME_TUXRF "TuxRF" +/** \brief Fuxrf CPU name */ +#define CPU_STR_NAME_FUXRF "FuxRF" +/** \brief Fuxusb CPU name */ +#define CPU_STR_NAME_FUXUSB "FuxUSB" +/** \brief Package release name */ +#define RELEASE_STR_NAME "Global release package" +/** \brief Unofficial package name */ +#define UNOFFICIAL_RELEASE_STR "Unofficial package" +/** \brief Number of retry count for the firmware versions retrieving */ +#define TUX_FIRMWARE_RETRY_COUNT 4 +#define TUX_FIRMWARE_GLOBAL_RETRY_COUNT 4 + +/** \brief CPU identifiers */ +typedef enum +{ + INVALID_CPU_NUM = -1, /**< Invalid CPU id */ + TUXCORE_CPU_NUM = LOWEST_CPU_NUM, /**< Tuxcore CPU id */ + TUXAUDIO_CPU_NUM, /**< Tuxaudio CPU id */ + TUXRF_CPU_NUM, /**< Tuxrf CPU id */ + FUXRF_CPU_NUM, /**< Fuxrf CPU id */ + FUXUSB_CPU_NUM, /**< Fuxusb CPU id */ +} CPU_IDENTIFIERS; + +/** \brief Versioning states. */ +typedef enum +{ + STDBY, /**< State machine STANDBY */ + INIT, /**< State machine Initialization */ + INFO_REQ, /**< State machine Request a firmware version*/ + INFO_GET, /**< State machine Get a firmware version */ + SPECIAL, /**< State machine Treats the special cases */ + RELEASE, /**< State machine Determine the package release version */ + FINALIZE, /**< State machine Finalization */ +} versioning_state_t; + +/** \brief Firmware descriptor structure */ +typedef struct +{ + CPU_IDENTIFIERS cpu_id; /**< CPU identifiant */ + unsigned int version_major; /**< Major version */ + unsigned int version_minor; /**< Minor version */ + unsigned int version_update; /**< Update version */ + unsigned int revision; /**< SVN revision */ + bool release; /**< Is released */ + bool local_modification; /**< Is a local modification */ + bool mixed_revisions; /**< Is a mixed modification */ + unsigned int author; /**< Author Id */ + unsigned int variation; /**< Variation Id */ + char version_string[VERSION_STRING_LENGTH]; + /**< String representation of a firmware version */ +} firmware_descriptor_t; + +/** \brief Array structure of firmware descriptors */ +typedef firmware_descriptor_t firmwares_descriptor_t[NUMBER_OF_CPU]; + +/** \brief Shared : Firmware descriptors */ +extern firmwares_descriptor_t firmwares_desc; +/** \brief Shared : Package release descriptor */ +extern firmware_descriptor_t firmware_release_desc; + +extern char knowed_tuxcore_symbolic_version[VERSION_STRING_LENGTH]; +extern char knowed_tuxaudio_symbolic_version[VERSION_STRING_LENGTH]; +extern char knowed_fuxusb_symbolic_version[VERSION_STRING_LENGTH]; +extern char knowed_fuxrf_symbolic_version[VERSION_STRING_LENGTH]; +extern char knowed_tuxrf_symbolic_version[VERSION_STRING_LENGTH]; + +extern void tux_firmware_init_descriptor(void); +extern void tux_firmware_update_version(void); +extern void tux_firmware_update_revision(void); +extern void tux_firmware_update_author(void); +extern void tux_firmware_state_machine_call(void); +extern void tux_firmware_get_descriptor(void); +extern bool tux_firmware_check_new_descriptor(bool save); + +#endif /* _TUX_FIRMWARE_H_ */ diff --git a/src/tux_flippers.c b/src/tux_flippers.c new file mode 100644 index 0000000..719edda --- /dev/null +++ b/src/tux_flippers.c @@ -0,0 +1,196 @@ +/* + * Tux Droid - Flippers + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_flippers.c + * \brief Flippers functions. + * \author remi.jocaille@c2me.be + * \ingroup flippers + */ + +#include + +#include "tux_cmd_parser.h" +#include "tux_hw_status.h" +#include "tux_hw_cmd.h" +#include "tux_movements.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" +#include "tux_flippers.h" + +/** Counter of flippers movements */ +static unsigned char mvmt_counter = 0; + +/** + * \brief Update the status of the position of the flippers. + */ +LIBLOCAL void +tux_flippers_update_position(void) +{ + char *new_position = ""; + + if (!hw_status_table.position2.flippers_down) + { + new_position = STRING_VALUE_DOWN; + } + else + { + new_position = STRING_VALUE_UP; + } + + tux_sw_status_set_strvalue(SW_ID_FLIPPERS_POSITION, new_position, true); +} + +/** + * \brief Update the status of the motor state of the flippers. + */ +LIBLOCAL void +tux_flippers_update_motor(void) +{ + unsigned char new_state; + + new_state = hw_status_table.position2.motors.bits.flippers_on; + tux_sw_status_set_intvalue(SW_ID_FLIPPERS_MOTOR_ON, new_state, true); +} + +/** + * \brief Update the status of the flippers movements remaining. + */ +LIBLOCAL void +tux_flippers_update_movements_remaining(void) +{ + unsigned char new_count; + + new_count = hw_status_table.position1.flippers_remaining_mvm; + + mvmt_counter = new_count; + tux_sw_status_set_intvalue(SW_ID_FLIPPERS_REMAINING_MVM, new_count, true); +} + +/** + * \brief Set the speed of the flippers movements. + * \param speed Movement speed. + * \return The command success result. + */ +LIBLOCAL bool +tux_flippers_cmd_speed(unsigned char speed) +{ + return tux_movement_perform(MOVE_FLIPPERS, 0, 0.0, speed, + FINAL_ST_UNDEFINED, true); +} + +/** + * \brief Execute a flippers on command. + * \param counter Number of flippers movements. + * \param final_state Desired final state of the flippers. + * \return The command success result. + */ +LIBLOCAL bool +tux_flippers_cmd_on(unsigned char counter, unsigned char final_state) +{ + return tux_movement_cmd_on(MOVE_FLIPPERS, counter, final_state); +} + +/** + * \brief Execute a flippers on command. + * \param timeout Duration of the movement. + * \param final_state Desired final state of the flippers. + * \return The command success result. + */ +LIBLOCAL bool +tux_flippers_cmd_on_during(float timeout, unsigned char final_state) +{ + bool ret; + data_frame frame = {FLIPPERS_WAVE_CMD, 0, 5, 0}; + delay_cmd_t cmd = { 0.0, TUX_CMD, FLIPPERS }; + + /* Short movements */ + if (timeout < 0.3) + { + return tux_movement_perform(MOVE_FLIPPERS, 0, timeout, 5, final_state, + false); + } + + /* Long movements */ + ret = tux_usb_send_to_tux(frame); + if (!ret) + { + return false; + } + + mvmt_counter = 255; + tux_sw_status_set_intvalue(SW_ID_FLIPPERS_REMAINING_MVM, mvmt_counter, true); + + switch (final_state) { + case FINAL_ST_UNDEFINED: + cmd.sub_command = OFF; + break; + case FINAL_ST_OPEN_UP: + cmd.sub_command = UP; + break; + case FINAL_ST_CLOSE_DOWN: + cmd.sub_command = DOWN; + break; + case FINAL_ST_STOP: + cmd.sub_command = OFF; + break; + } + ret = tux_cmd_parser_insert_sys_command(timeout, &cmd); + + return ret; +} + +/** + * \brief Raise the flippers. + * \return The command success result. + */ +LIBLOCAL bool +tux_flippers_cmd_up(void) +{ + return tux_movement_perform(MOVE_FLIPPERS, 0, 0, 5, FINAL_ST_OPEN_UP, false); +} + +/** + * \brief Lower the flippers. + * \return The command success result. + */ +LIBLOCAL bool +tux_flippers_cmd_down(void) +{ + return tux_movement_perform(MOVE_FLIPPERS, 0, 0, 5, FINAL_ST_CLOSE_DOWN, false); +} + +/** + * \brief Stop the flippers movement. + * \return The command success result. + */ +LIBLOCAL bool +tux_flippers_cmd_off(void) +{ + bool ret; + + tux_cmd_parser_clean_sys_command(FLIPPERS); + ret = tux_movement_perform(MOVE_FLIPPERS, 0, 0, 5, FINAL_ST_STOP, false); + mvmt_counter = 0; + tux_sw_status_set_intvalue(SW_ID_FLIPPERS_REMAINING_MVM, mvmt_counter, true); + + return ret; +} diff --git a/src/tux_flippers.h b/src/tux_flippers.h new file mode 100644 index 0000000..56d9437 --- /dev/null +++ b/src/tux_flippers.h @@ -0,0 +1,48 @@ +/* + * Tux Droid - Flippers + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_flippers.h + * \brief Flippers header. + * \author remi.jocaille@c2me.be + * \ingroup flippers + */ + +#ifndef _TUX_FLIPPERS_H_ +#define _TUX_FLIPPERS_H_ + +#include + +/** \brief Flippers position down */ +#define TUX_FLIPPERS_POSITION_DOWN 0 +/** \brief Flippers position up */ +#define TUX_FLIPPERS_POSITION_UP 1 + +extern void tux_flippers_update_position(void); +extern void tux_flippers_update_motor(void); +extern void tux_flippers_update_movements_remaining(void); +extern bool tux_flippers_cmd_speed(unsigned char speed); +extern bool tux_flippers_cmd_on(unsigned char counter, unsigned char final_state); +extern bool tux_flippers_cmd_on_during(float timeout, unsigned char final_state); +extern bool tux_flippers_cmd_up(void); +extern bool tux_flippers_cmd_down(void); +extern bool tux_flippers_cmd_off(void); + +#endif /* _TUX_FLIPPERS_H_ */ diff --git a/src/tux_hid_unix.c b/src/tux_hid_unix.c new file mode 100644 index 0000000..7c63ebe --- /dev/null +++ b/src/tux_hid_unix.c @@ -0,0 +1,269 @@ +/* + * Tux Droid - Hid interface (only for unix) + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_hid_unix.c + * \brief Tux HID functions. + * \author remi.jocaille@c2me.be + * \ingroup hid_interface + */ + +#ifndef WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tux_hid_unix.h" +#include "tux_misc.h" + +static int tux_device_hdl = -1; +static char tux_device_path[256] = ""; +static struct hiddev_usage_ref uref_out; +static struct hiddev_report_info rinfo_out; + +/** + * \brief Search the HID dongle in a "dev" path. + * \param path Path how to search. + * \param vendor_id Dongle vendor ID. + * \param product_id Dongle product ID. + * \return true or false + */ +static bool +find_dongle_from_path(const char *path, int vendor_id, int product_id) +{ + DIR* dir; + struct dirent *dinfo; + int fd = -1; + char device_path[256] = ""; + struct hiddev_devinfo device_info; + int err; + + dir = opendir(path); + if (dir != NULL) + { + while ((dinfo = readdir(dir)) != NULL) + { + if (strncmp(dinfo->d_name, "hiddev", 6) == 0) + { + sprintf(device_path, "%s/%s", path, dinfo->d_name); + + if ((fd = open(device_path, O_RDONLY)) >= 0) + { + err = ioctl(fd, HIDIOCGDEVINFO, &device_info); + if ((device_info.vendor == vendor_id) && + ((device_info.product & 0xFFFF) == product_id)) + { + sprintf(tux_device_path, "%s", device_path); + tux_device_hdl = fd; + + closedir(dir); + + return true; + } + else + { + close(fd); + } + } + } + } + + closedir(dir); + } + + return false; +} + +/** + * \brief Check if the dongle is still plugged. + * \return true or false. + */ +static bool +check_device_still_plugged(void) +{ + FILE *fp; + + if (tux_device_hdl == -1) + { + return false; + } + else + { + fp = fopen(tux_device_path, "r"); + if (fp) + { + fclose(fp); + return true; + } + else + { + return false; + } + } +} + +/** + * \brief Capture the HID dongle. + * \param vendor_id Dongle vendor ID. + * \param product_id Dongle product ID. + * \return true or false. + */ +bool LIBLOCAL +tux_hid_capture(int vendor_id, int product_id) +{ + /* Normal path to scan is /dev/usb */ + if (find_dongle_from_path("/dev/usb", vendor_id, product_id)) + { + return true; + } + + /* Other possible path to scan is /dev/usb */ + if (find_dongle_from_path("/dev", vendor_id, product_id)) + { + return true; + } + + /* dongle not found */ + return false; +} + +/** + * \brief Release the access to the HID dongle. + */ +void LIBLOCAL +tux_hid_release(void) +{ + if (tux_device_hdl != -1) + { + close(tux_device_hdl); + tux_device_hdl = -1; + } +} + +/** + * \brief Write data to the HID dongle. + * \param size Data size. + * \param buffer Data to write. + * \return The write success. + */ +bool LIBLOCAL +tux_hid_write(int size, const char *buffer) +{ + int i; + int err; + + if (!check_device_still_plugged()) + { + return false; + } + + rinfo_out.report_type = HID_REPORT_TYPE_OUTPUT; + rinfo_out.report_id = HID_REPORT_ID_FIRST; + + err = ioctl(tux_device_hdl, HIDIOCGREPORTINFO, &rinfo_out); + if (err < 0) + { + return false; + } + + for(i = 0; i < size; i++) + { + uref_out.report_type = HID_REPORT_TYPE_OUTPUT; + uref_out.report_id = HID_REPORT_ID_FIRST; + uref_out.usage_index = i; + uref_out.value = (unsigned char)buffer[i]; + + err = ioctl(tux_device_hdl,HIDIOCSUSAGE, &uref_out); + if (err < 0) + { + return false; + } + } + + err = ioctl(tux_device_hdl,HIDIOCSREPORT,&rinfo_out); + if (err < 0) + { + return false; + } + + return true; +} + +/** + * \brief Read data from the HID dongle. + * \param size Data size. + * \param buffer Data buffer. + * \return The read success. + */ +bool LIBLOCAL +tux_hid_read(int size, char *buffer) +{ + int i; + int err; + + if (!check_device_still_plugged()) + { + return false; + } + + rinfo_out.report_type = HID_REPORT_TYPE_INPUT; + rinfo_out.report_id = HID_REPORT_ID_FIRST; + + err = ioctl(tux_device_hdl, HIDIOCGREPORTINFO, &rinfo_out); + if (err < 0) + { + return false; + } + + for (i = 0; i < size; i++) + { + uref_out.report_type = HID_REPORT_TYPE_INPUT; + uref_out.report_id = HID_REPORT_ID_FIRST; + uref_out.usage_index = i; + + err = ioctl(tux_device_hdl, HIDIOCGUCODE, &uref_out); + if (err < 0) + { + return false; + } + + err = ioctl(tux_device_hdl, HIDIOCGUSAGE, &uref_out); + if (err < 0) + { + return false; + } + + buffer[i] = uref_out.value; + } + + return true; +} + +#endif /* Not WIN32 */ diff --git a/src/tux_hid_unix.h b/src/tux_hid_unix.h new file mode 100644 index 0000000..1278514 --- /dev/null +++ b/src/tux_hid_unix.h @@ -0,0 +1,46 @@ +/* + * Tux Droid - Hid interface (only for unix) + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_hid_unix.h + * \brief Tux HID header. + * \author remi.jocaille@c2me.be + * \ingroup hid_interface + */ + +#ifndef WIN32 + +#ifndef _TUX_HID_H_ +#define _TUX_HID_H_ + +#include + +/** \brief HID USB timout */ +#define HID_RW_TIMEOUT 1000 + +extern bool tux_hid_capture(int vendor_id, int product_id); +extern void tux_hid_release(void); +extern bool tux_hid_write(int size, const char *buffer); +extern bool tux_hid_read(int size, char *buffer); + +#endif /* _TUX_HID_H_ */ + +#endif /* Not WIN32 */ + diff --git a/src/tux_hid_win32.c b/src/tux_hid_win32.c new file mode 100644 index 0000000..8b1f1f5 --- /dev/null +++ b/src/tux_hid_win32.c @@ -0,0 +1,240 @@ +/* + * Tux Droid - Hid interface (only for windows) + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_hid_win32.c + * \brief Tux HID functions. + * \author remi.jocaille@c2me.be + * \ingroup hid_interface + */ + +#ifdef WIN32 + +#include +#include +#include +#include +#include +#include + +#include "tux_hid_win32.h" +#include "tux_misc.h" + +static char device_symbolic_name[256] = ""; +static HANDLE tux_device_hdl = NULL; +static COMMTIMEOUTS timeout; + +/** + * \brief Capture the HID dongle. + * \param vendor_id Dongle vendor ID. + * \param product_id Dongle product ID. + * \return true or false. + */ +bool LIBLOCAL +tux_hid_capture(int vendor_id, int product_id) +{ + GUID hid_guid; + HANDLE h_dev_info; + SP_DEVICE_INTERFACE_DATA dev_info_data; + PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL; + int member_index = 0; + bool last_device = false; + long result; + unsigned long length = 0; + ULONG required; + HANDLE device_hdl = NULL; + HIDD_ATTRIBUTES attributes; + bool tux_found = false;; + + if (tux_device_hdl != NULL) + { + return false; + } + + HidD_GetHidGuid(&hid_guid); + + h_dev_info = SetupDiGetClassDevs(&hid_guid, + NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + + dev_info_data.cbSize = sizeof(dev_info_data); + + member_index = 0; + + do + { + result = SetupDiEnumDeviceInterfaces(h_dev_info, 0, + &hid_guid, member_index, &dev_info_data); + + if (result != 0) + { + result = SetupDiGetDeviceInterfaceDetail(h_dev_info, + &dev_info_data, NULL, 0, &length, NULL); + + detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(length); + detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + result = SetupDiGetDeviceInterfaceDetail(h_dev_info, + &dev_info_data, detail_data, length, &required, NULL); + + device_hdl = CreateFile(detail_data->DevicePath, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, 0, NULL); + + attributes.Size = sizeof(attributes); + result = HidD_GetAttributes(device_hdl, &attributes); + + if ((attributes.VendorID == vendor_id) && + (attributes.ProductID == product_id)) + { + sprintf(device_symbolic_name, "%s", detail_data->DevicePath); + + CloseHandle(device_hdl); + + tux_device_hdl = CreateFile(detail_data->DevicePath, + GENERIC_WRITE|GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, 0, NULL); + + timeout.ReadTotalTimeoutConstant = HID_RW_TIMEOUT; + timeout.WriteTotalTimeoutConstant = HID_RW_TIMEOUT; + SetCommTimeouts(tux_device_hdl, &timeout); + + tux_found = true; + + break; + } + + CloseHandle(device_hdl); + free(detail_data); + } + else + { + last_device = true; + } + + member_index++; + + } + while (last_device == false); + + SetupDiDestroyDeviceInfoList(h_dev_info); + + if (tux_found) + { + return true; + } + else + { + return false; + } +} + +/** + * \brief Release the access to the HID dongle. + */ +void LIBLOCAL +tux_hid_release(void) +{ + if (tux_device_hdl != NULL) + { + CloseHandle(tux_device_hdl); + tux_device_hdl = NULL; + } +} + +/** + * \brief Write data to the HID dongle. + * \param size Data size. + * \param buffer Data to write. + * \return The write success. + */ +bool LIBLOCAL +tux_hid_write(int size, const char *buffer) +{ + long wrt_count; + + char report[REPORT_SIZE_OUT + 1] = { [0 ... REPORT_SIZE_OUT] = 0 }; + long result; + + if (size > REPORT_SIZE_OUT) + { + return false; + } + + if (tux_device_hdl == NULL) + { + return false; + } + + report[0] = 0; + memcpy(&report[1], buffer, size); + + result = WriteFile(tux_device_hdl, report, REPORT_SIZE_OUT + 1, &wrt_count, NULL); + + if (!result) + { + return false; + } + else + { + return true; + } +} + +/** + * \brief Read data from the HID dongle. + * \param size Data size. + * \param buffer Data buffer. + * \return The read success. + */ +bool LIBLOCAL +tux_hid_read(int size, char *buffer) +{ + long rd_count; + char report[REPORT_SIZE_IN + 1]; + long result; + + if (size > REPORT_SIZE_IN) + { + return false; + } + + if (tux_device_hdl == NULL) + { + return false; + } + + result = ReadFile(tux_device_hdl, report, REPORT_SIZE_IN + 1, &rd_count, + NULL); + + memcpy(buffer, &report[1], size); + + if (!result) + { + return false; + } + else + { + return true; + } +} + +#endif /* WIN32 */ diff --git a/src/tux_hid_win32.h b/src/tux_hid_win32.h new file mode 100644 index 0000000..eebe332 --- /dev/null +++ b/src/tux_hid_win32.h @@ -0,0 +1,47 @@ +/* + * Tux Droid - Hid interface (only for windows) + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_hid_win32.h + * \brief Tux HID header. + * \author remi.jocaille@c2me.be + * \ingroup hid_interface + */ + +#ifdef WIN32 + +#ifndef _TUX_HID_H_ +#define _TUX_HID_H_ + +/** \brief HID USB timout */ +#define HID_RW_TIMEOUT 1000 +/** \brief HID input report size */ +#define REPORT_SIZE_IN 64 +/** \brief HID output report size */ +#define REPORT_SIZE_OUT 64 + +extern bool tux_hid_capture(int vendor_id, int product_id); +extern void tux_hid_release(void); +extern bool tux_hid_write(int size, const char *buffer); +extern bool tux_hid_read(int size, char *buffer); + +#endif /* _TUX_HID_H_ */ + +#endif /* WIN32 */ diff --git a/src/tux_hw_cmd.h b/src/tux_hw_cmd.h new file mode 100644 index 0000000..cc88b8a --- /dev/null +++ b/src/tux_hw_cmd.h @@ -0,0 +1,96 @@ +/* + * Tux Droid - Low level commands + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_HW_CMD_H_ +#define _TUX_HW_CMD_H_ + +typedef enum +{ + USB_HEADER_TUX = 0, + USB_HEADER_DONGLE = 1, + USB_HEADER_BOOTLOADER = 2, +} usb_command_header_t; + +typedef enum +{ + USB_DONGLE_CONNECTION_CMD = 0, + USB_DONGLE_STATUS_CMD = 1, + USB_DONGLE_AUDIO_CMD = 2, + USB_DONGLE_VERSION_CMD = 6, +} usb_dongle_commands_t; + +typedef enum +{ + USB_TUX_CONNECTION_DISCONNECT = 1, + USB_TUX_CONNECTION_CONNECT = 2, + USB_TUX_CONNECTION_ID_REQUEST = 3, + USB_TUX_CONNECTION_ID_LOOKUP = 4, + USB_TUX_CONNECTION_CHANGE_ID = 5, + USB_TUX_CONNECTION_WAKEUP = 6, + USB_TUX_CONNECTION_WIRELESS_CHANNEL = 7, +} usb_tux_connection_t; + +typedef enum +{ + EYES_OPEN_CMD = 0x33, + EYES_CLOSE_CMD = 0x38, + EYES_BLINK_CMD = 0x40, + EYES_STOP_CMD = 0x32, + + MOUTH_OPEN_CMD = 0x34, + MOUTH_CLOSE_CMD = 0x35, + MOUTH_MOVE_CMD = 0x41, + MOUTH_STOP_CMD = 0x36, + + FLIPPERS_RAISE_CMD = 0x39, + FLIPPERS_LOWER_CMD = 0x3A, + FLIPPERS_WAVE_CMD = 0x80, + FLIPPERS_STOP_CMD = 0x30, + + SPIN_LEFT_CMD = 0x83, + SPIN_RIGHT_CMD = 0x82, + SPIN_STOP_CMD = 0x37, + + LED_FADE_SPEED_CMD = 0xD0, + LED_SET_CMD = 0xD1, + LED_PULSE_RANGE_CMD = 0xD2, + LED_PULSE_CMD = 0xD3, + + TUX_PONG_PING_CMD = 0x7F, + + MOTORS_SET_CMD = 0xD4, + MOTORS_CONFIG_CMD = 0x81, + + TURN_IR_ON_CMD = 0x17, + TURN_IR_OFF_CMD = 0x18, + IR_SEND_RC5_CMD = 0x91, + + PLAY_SOUND_CMD = 0x90, + STORE_SOUND_CMD = 0x52, + CONFIRM_STORAGE_CMD = 0x53, + ERASE_FLASH_CMD = 0x54, + + AUDIO_MUTE_CMD = 0x92, + + WIRELESS_FREQ_BOUNDARIES_CMD = 0x88 +} usb_tux_commands_t; + + +#endif /* _TUX_HW_CMD_H_ */ diff --git a/src/tux_hw_status.c b/src/tux_hw_status.c new file mode 100644 index 0000000..0c0e3a1 --- /dev/null +++ b/src/tux_hw_status.c @@ -0,0 +1,657 @@ +/* + * Tux Droid - Low level status + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include + +#include "log.h" +#include "tux_hw_status.h" +#include "tux_misc.h" + +LIBLOCAL hw_status_table_t hw_status_table; +LIBLOCAL unsigned char tux_hw_status_header_counter[16] = { 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static int parse_body_ports(const unsigned char *frame); +static int parse_body_sensors1(const unsigned char *frame); +static int parse_body_light(const unsigned char *frame); +static int parse_body_position1(const unsigned char *frame); +static int parse_body_position2(const unsigned char *frame); +static int parse_body_ir(const unsigned char *frame); +static int parse_body_id(const unsigned char *frame); +static int parse_body_battery(const unsigned char *frame); +static int parse_body_version(const unsigned char *frame); +static int parse_body_revision(const unsigned char *frame); +static int parse_body_author(const unsigned char *frame); +static int parse_body_audio(const unsigned char *frame); +static int parse_body_sound_var(const unsigned char *frame); +static int parse_body_flash_prog(const unsigned char *frame); +static int parse_body_led(const unsigned char *frame); +static int parse_body_pong(const unsigned char *frame); + +/** + * + */ +LIBLOCAL void +tux_hw_status_init(void) +{ + memset(&hw_status_table, 0, sizeof(hw_status_table_t)); +} + +/** + * + */ +LIBLOCAL int +tux_hw_status_parse_frame(const unsigned char *frame) +{ + switch(frame[0]) { + case FRAME_HEADER_PORTS: + tux_hw_status_header_counter[ID_FRAME_HEADER_PORTS]++; + return parse_body_ports(frame); + case FRAME_HEADER_SENSORS1: + tux_hw_status_header_counter[ID_FRAME_HEADER_SENSORS1]++; + return parse_body_sensors1(frame); + case FRAME_HEADER_LIGHT: + tux_hw_status_header_counter[ID_FRAME_HEADER_LIGHT]++; + return parse_body_light(frame); + case FRAME_HEADER_POSITION1: + tux_hw_status_header_counter[ID_FRAME_HEADER_POSITION1]++; + return parse_body_position1(frame); + case FRAME_HEADER_POSITION2: + tux_hw_status_header_counter[ID_FRAME_HEADER_POSITION2]++; + return parse_body_position2(frame); + case FRAME_HEADER_IR: + tux_hw_status_header_counter[ID_FRAME_HEADER_IR]++; + return parse_body_ir(frame); + case FRAME_HEADER_ID: + tux_hw_status_header_counter[ID_FRAME_HEADER_ID]++; + return parse_body_id(frame); + case FRAME_HEADER_BATTERY: + tux_hw_status_header_counter[ID_FRAME_HEADER_BATTERY]++; + return parse_body_battery(frame); + case FRAME_HEADER_VERSION: + tux_hw_status_header_counter[ID_FRAME_HEADER_VERSION]++; + return parse_body_version(frame); + case FRAME_HEADER_REVISION: + tux_hw_status_header_counter[ID_FRAME_HEADER_REVISION]++; + return parse_body_revision(frame); + case FRAME_HEADER_AUTHOR: + tux_hw_status_header_counter[ID_FRAME_HEADER_AUTHOR]++; + return parse_body_author(frame); + case FRAME_HEADER_AUDIO: + tux_hw_status_header_counter[ID_FRAME_HEADER_AUDIO]++; + return parse_body_audio(frame); + case FRAME_HEADER_SOUND_VAR: + tux_hw_status_header_counter[ID_FRAME_HEADER_SOUND_VAR]++; + return parse_body_sound_var(frame); + case FRAME_HEADER_FLASH_PROG: + tux_hw_status_header_counter[ID_FRAME_HEADER_FLASH_PROG]++; + return parse_body_flash_prog(frame); + case FRAME_HEADER_LED: + tux_hw_status_header_counter[ID_FRAME_HEADER_LED]++; + return parse_body_led(frame); + case FRAME_HEADER_PONG: + tux_hw_status_header_counter[ID_FRAME_HEADER_PONG]++; + return parse_body_pong(frame); + default: + return -1; + } +} + +/** + * + */ +LIBLOCAL char * +tux_hw_status_id_to_str(const unsigned char id) +{ + switch(id) { + case ID_FRAME_HEADER_PORTS: + return "PORTS"; + case ID_FRAME_HEADER_SENSORS1: + return "SENSORS1"; + case ID_FRAME_HEADER_LIGHT: + return "LIGHT"; + case ID_FRAME_HEADER_POSITION1: + return "POSITION1"; + case ID_FRAME_HEADER_POSITION2: + return "POSITION2"; + case ID_FRAME_HEADER_IR: + return "IR"; + case ID_FRAME_HEADER_ID: + return "ID"; + case ID_FRAME_HEADER_BATTERY: + return "BATTERY"; + case ID_FRAME_HEADER_VERSION: + return "VERSION"; + case ID_FRAME_HEADER_REVISION: + return "REVISION"; + case ID_FRAME_HEADER_AUTHOR: + return "AUTHOR"; + case ID_FRAME_HEADER_AUDIO: + return "AUDIO"; + case ID_FRAME_HEADER_SOUND_VAR: + return "SOUND_VAR"; + case ID_FRAME_HEADER_FLASH_PROG: + return "FLASH_PROG"; + case ID_FRAME_HEADER_LED: + return "LED"; + case ID_FRAME_HEADER_PONG: + return "PONG"; + default: + return "NDEF"; + } +} + +/** + * + */ +LIBLOCAL void +tux_hw_status_header_counter_check(void) +{ + int i; + unsigned char p_count = 0; + + for (i = 0; i < 16; i++) + { + p_count += tux_hw_status_header_counter[i]; + } + + /* as there is only debug code in this for loop it might make sense + to have a guarding ifdef around the for loop to save a few cycles */ + if (p_count >= 15) + { + log_debug("Frames counter (%d) :\n\t%s:[%d]\n\t%s:[%d]\n\ + \r\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\ + \r\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\ + \r\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n\t%s:[%d]\n", + p_count, + tux_hw_status_id_to_str(0), tux_hw_status_header_counter[0], + tux_hw_status_id_to_str(1), tux_hw_status_header_counter[1], + tux_hw_status_id_to_str(2), tux_hw_status_header_counter[2], + tux_hw_status_id_to_str(3), tux_hw_status_header_counter[3], + tux_hw_status_id_to_str(4), tux_hw_status_header_counter[4], + tux_hw_status_id_to_str(5), tux_hw_status_header_counter[5], + tux_hw_status_id_to_str(6), tux_hw_status_header_counter[6], + tux_hw_status_id_to_str(7), tux_hw_status_header_counter[7], + tux_hw_status_id_to_str(8), tux_hw_status_header_counter[8], + tux_hw_status_id_to_str(9), tux_hw_status_header_counter[9], + tux_hw_status_id_to_str(10), tux_hw_status_header_counter[10], + tux_hw_status_id_to_str(11), tux_hw_status_header_counter[11], + tux_hw_status_id_to_str(12), tux_hw_status_header_counter[12], + tux_hw_status_id_to_str(13), tux_hw_status_header_counter[13], + tux_hw_status_id_to_str(14), tux_hw_status_header_counter[14], + tux_hw_status_id_to_str(15), tux_hw_status_header_counter[15]); + } + + // use memset instead ? + for (i = 0; i < 16; i++) + { + tux_hw_status_header_counter[i] = 0; + } +} + +/** + * + */ +static int +parse_body_ports(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.ports.portb.Byte != frame[1]) + { + ret = FRAME_HEADER_PORTS; + } + if (hw_status_table.ports.portc.Byte != frame[2]) + { + ret = FRAME_HEADER_PORTS; + } + if (hw_status_table.ports.portd.Byte != frame[3]) + { + ret = FRAME_HEADER_PORTS; + } + + hw_status_table.ports.portb.Byte = frame[1]; + hw_status_table.ports.portc.Byte = frame[2]; + hw_status_table.ports.portd.Byte = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_sensors1(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.sensors1.sensors.Byte != frame[1]) + { + ret = FRAME_HEADER_SENSORS1; + } + if (hw_status_table.sensors1.play_internal_sound != frame[2]) + { + ret = FRAME_HEADER_SENSORS1; + } + if (hw_status_table.sensors1.play_general_sound != frame[3]) + { + ret = FRAME_HEADER_SENSORS1; + } + + hw_status_table.sensors1.sensors.Byte = frame[1]; + hw_status_table.sensors1.play_internal_sound = frame[2]; + hw_status_table.sensors1.play_general_sound = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_light(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.light.high_level != frame[1]) + { + ret = FRAME_HEADER_LIGHT; + } + if (hw_status_table.light.low_level != frame[2]) + { + ret = FRAME_HEADER_LIGHT; + } + if (hw_status_table.light.mode != frame[3]) + { + ret = FRAME_HEADER_LIGHT; + } + + hw_status_table.light.high_level = frame[1]; + hw_status_table.light.low_level = frame[2]; + hw_status_table.light.mode = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_position1(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.position1.eyes_remaining_mvm != frame[1]) + { + ret = FRAME_HEADER_POSITION1; + } + if (hw_status_table.position1.mouth_remaining_mvm != frame[2]) + { + ret = FRAME_HEADER_POSITION1; + } + if (hw_status_table.position1.flippers_remaining_mvm != frame[3]) + { + ret = FRAME_HEADER_POSITION1; + } + + hw_status_table.position1.eyes_remaining_mvm = frame[1]; + hw_status_table.position1.mouth_remaining_mvm = frame[2]; + hw_status_table.position1.flippers_remaining_mvm = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_position2(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.position2.spin_remaining_mvm != frame[1]) + { + ret = FRAME_HEADER_POSITION2; + } + if (hw_status_table.position2.flippers_down != frame[2]) + { + ret = FRAME_HEADER_POSITION2; + } + if (hw_status_table.position2.motors.Byte != frame[3]) + { + ret = FRAME_HEADER_POSITION2; + } + + hw_status_table.position2.spin_remaining_mvm = frame[1]; + hw_status_table.position2.flippers_down = frame[2]; + hw_status_table.position2.motors.Byte = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_ir(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.ir.rc5_code.Byte != frame[1]) + { + ret = FRAME_HEADER_IR; + } + + hw_status_table.ir.rc5_code.Byte = frame[1]; + + return ret; +} + +/** + * + */ +static int +parse_body_id(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.id.msb_number != frame[1]) + { + ret = FRAME_HEADER_ID; + } + if (hw_status_table.id.lsb_number != frame[2]) + { + ret = FRAME_HEADER_ID; + } + + hw_status_table.id.msb_number = frame[1]; + hw_status_table.id.lsb_number = frame[2]; + + return ret; +} + +/** + * + */ +static int +parse_body_battery(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.battery.high_level != frame[1]) + { + ret = FRAME_HEADER_BATTERY; + } + if (hw_status_table.battery.low_level != frame[2]) + { + ret = FRAME_HEADER_BATTERY; + } + if (hw_status_table.battery.motors_state != frame[3]) + { + ret = FRAME_HEADER_BATTERY; + } + + hw_status_table.battery.high_level = frame[1]; + hw_status_table.battery.low_level = frame[2]; + hw_status_table.battery.motors_state = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_version(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.version.cm.Byte != frame[1]) + { + ret = FRAME_HEADER_VERSION; + } + if (hw_status_table.version.minor != frame[2]) + { + ret = FRAME_HEADER_VERSION; + } + if (hw_status_table.version.update != frame[3]) + { + ret = FRAME_HEADER_VERSION; + } + + hw_status_table.version.cm.Byte = frame[1]; + hw_status_table.version.minor = frame[2]; + hw_status_table.version.update = frame[3]; + + return ret; +} + +/** + * + */ +LIBLOCAL void +tux_hw_parse_body_version(const unsigned char *frame) +{ + parse_body_version(frame); +} + +/** + * + */ +static int +parse_body_revision(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.revision.lsb_number != frame[1]) + { + ret = FRAME_HEADER_REVISION; + } + if (hw_status_table.revision.msb_number != frame[2]) + { + ret = FRAME_HEADER_REVISION; + } + if (hw_status_table.revision.release_type.Byte != frame[3]) + { + ret = FRAME_HEADER_REVISION; + } + + hw_status_table.revision.lsb_number = frame[1]; + hw_status_table.revision.msb_number = frame[2]; + hw_status_table.revision.release_type.Byte = frame[3]; + + return ret; +} + +/** + * + */ +LIBLOCAL void +tux_hw_parse_body_revision(const unsigned char *frame) +{ + parse_body_revision(frame); +} + +/** + * + */ +static int +parse_body_author(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.author.lsb_id != frame[1]) + { + ret = FRAME_HEADER_AUTHOR; + } + if (hw_status_table.author.msb_id != frame[2]) + { + ret = FRAME_HEADER_AUTHOR; + } + if (hw_status_table.author.variation_number != frame[3]) + { + ret = FRAME_HEADER_AUTHOR; + } + + hw_status_table.author.lsb_id = frame[1]; + hw_status_table.author.msb_id = frame[2]; + hw_status_table.author.variation_number = frame[3]; + + return ret; +} + +/** + * + */ +LIBLOCAL void +tux_hw_parse_body_author(const unsigned char *frame) +{ + parse_body_author(frame); +} + +/** + * + */ +static int +parse_body_audio(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.audio.sound_track_played != frame[1]) + { + ret = FRAME_HEADER_AUDIO; + } + if (hw_status_table.audio.programming_steps.Byte != frame[2]) + { + ret = FRAME_HEADER_AUDIO; + } + if (hw_status_table.audio.programmed_sound_track != frame[3]) + { + ret = FRAME_HEADER_AUDIO; + } + + hw_status_table.audio.sound_track_played = frame[1]; + hw_status_table.audio.programming_steps.Byte = frame[2]; + hw_status_table.audio.programmed_sound_track = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_sound_var(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.sound_var.number_of_sounds != frame[1]) + { + ret = FRAME_HEADER_SOUND_VAR; + } + if (hw_status_table.sound_var.flash_usage != frame[2]) + { + ret = FRAME_HEADER_SOUND_VAR; + } + + hw_status_table.sound_var.number_of_sounds = frame[1]; + hw_status_table.sound_var.flash_usage = frame[2]; + + return ret; +} + +/** + * + */ +static int +parse_body_flash_prog(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.flash_prog.current_state != frame[1]) + { + ret = FRAME_HEADER_FLASH_PROG; + } + if (hw_status_table.flash_prog.last_sound_size != frame[2]) + { + ret = FRAME_HEADER_FLASH_PROG; + } + + hw_status_table.flash_prog.current_state = frame[1]; + hw_status_table.flash_prog.last_sound_size = frame[2]; + + return ret; +} + +/** + * + */ +static int +parse_body_led(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.led.left_led_intensity != frame[1]) + { + ret = FRAME_HEADER_LED; + } + if (hw_status_table.led.right_led_intensity != frame[2]) + { + ret = FRAME_HEADER_LED; + } + if (hw_status_table.led.effect_status.Byte != frame[3]) + { + ret = FRAME_HEADER_LED; + } + + hw_status_table.led.left_led_intensity = frame[1]; + hw_status_table.led.right_led_intensity = frame[2]; + hw_status_table.led.effect_status.Byte = frame[3]; + + return ret; +} + +/** + * + */ +static int +parse_body_pong(const unsigned char *frame) +{ + int ret = 0; + + if (hw_status_table.pong.pongs_pending_number != frame[1]) + { + ret = FRAME_HEADER_PONG; + } + if (hw_status_table.pong.pongs_lost_by_i2c_number != frame[2]) + { + ret = FRAME_HEADER_PONG; + } + if (hw_status_table.pong.pongs_lost_by_rf_number != frame[3]) + { + ret = FRAME_HEADER_PONG; + } + + hw_status_table.pong.pongs_pending_number = frame[1]; + hw_status_table.pong.pongs_lost_by_i2c_number = frame[2]; + hw_status_table.pong.pongs_lost_by_rf_number = frame[3]; + + return ret; +} diff --git a/src/tux_hw_status.h b/src/tux_hw_status.h new file mode 100644 index 0000000..e3cd5f7 --- /dev/null +++ b/src/tux_hw_status.h @@ -0,0 +1,365 @@ +/* + * Tux Droid - Low level status + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_HW_STATUS_H_ +#define _TUX_HW_STATUS_H_ + +#define FRAME_HEADER_PORTS 0xC0 +#define FRAME_HEADER_SENSORS1 0xC1 +#define FRAME_HEADER_LIGHT 0xC2 +#define FRAME_HEADER_POSITION1 0xC3 +#define FRAME_HEADER_POSITION2 0xC4 +#define FRAME_HEADER_IR 0xC5 +#define FRAME_HEADER_ID 0xC6 +#define FRAME_HEADER_BATTERY 0xC7 +#define FRAME_HEADER_VERSION 0xC8 +#define FRAME_HEADER_REVISION 0xC9 +#define FRAME_HEADER_AUTHOR 0xCA +#define FRAME_HEADER_SOUND_VAR 0xCB +#define FRAME_HEADER_AUDIO 0xCC +#define FRAME_HEADER_FLASH_PROG 0xCD +#define FRAME_HEADER_LED 0xCE +#define FRAME_HEADER_PONG 0xFF + +typedef enum +{ + ID_FRAME_HEADER_PORTS = 0, + ID_FRAME_HEADER_SENSORS1 = 1, + ID_FRAME_HEADER_LIGHT = 2, + ID_FRAME_HEADER_POSITION1 = 3, + ID_FRAME_HEADER_POSITION2 = 4, + ID_FRAME_HEADER_IR = 5, + ID_FRAME_HEADER_ID = 6, + ID_FRAME_HEADER_BATTERY = 7, + ID_FRAME_HEADER_VERSION = 8, + ID_FRAME_HEADER_REVISION = 9, + ID_FRAME_HEADER_AUTHOR = 10, + ID_FRAME_HEADER_SOUND_VAR = 11, + ID_FRAME_HEADER_AUDIO = 12, + ID_FRAME_HEADER_FLASH_PROG = 13, + ID_FRAME_HEADER_LED = 14, + ID_FRAME_HEADER_PONG = 15, +} headers_id_t; + +typedef unsigned char _BIT_; + +typedef struct +{ + _BIT_ flippers_motor_backward:1; + _BIT_ spin_motor_forward:1; + _BIT_ spin_motor_backward:1; + _BIT_ mouth_open_switch:1; + _BIT_ mouth_closed_switch:1; + _BIT_ head_push_switch:1; + _BIT_ charger_inhibit_signal:1; + _BIT_ external_io:1; +} _PORTB_BITS_; + +typedef union +{ + unsigned char Byte; + _PORTB_BITS_ bits; +} _PORTB_BYTE_; + +typedef struct +{ + _BIT_ photo_transistor_pull_up:1; + _BIT_ flippers_position_switch:1; + _BIT_ right_blue_led:1; + _BIT_ left_blue_led:1; + _BIT_ i2c_sda_line:1; + _BIT_ i2c_scl_line:1; + _BIT_ reset:1; + _BIT_ ndef:1; +} _PORTC_BITS_; + +typedef union +{ + unsigned char Byte; + _PORTC_BITS_ bits; +} _PORTC_BYTE_; + +typedef struct +{ + _BIT_ head_motor_for_mouth:1; + _BIT_ head_motor_for_eyes:1; + _BIT_ ir_receiver_signal:1; + _BIT_ spin_position_switch:1; + _BIT_ flippers_motor_forward:1; + _BIT_ ir_led:1; + _BIT_ eyes_open_switch:1; + _BIT_ eyes_closed_switch:1; +} _PORTD_BITS_; + +typedef union +{ + unsigned char Byte; + _PORTD_BITS_ bits; +} _PORTD_BYTE_; + +typedef struct +{ + _BIT_ left_wing_push_button:1; + _BIT_ right_wing_push_button:1; + _BIT_ power_plug_insertion_switch:1; + _BIT_ head_push_button:1; + _BIT_ charger_led_status:1; + _BIT_ rf_connection_status:1; + _BIT_ internal_power_switch:1; + _BIT_ mute_status:1; +} _SENSORS_BITS_; + +typedef union +{ + unsigned char Byte; + _SENSORS_BITS_ bits; +} _SENSORS_BYTE_; + +typedef struct +{ + _BIT_ command:6; + _BIT_ toggle:1; + _BIT_ received_flag:1; +} _RC5_BITS_; + +typedef union +{ + unsigned char Byte; + _RC5_BITS_ bits; +} _RC5_BYTE_; + +typedef struct +{ + _BIT_ cpu_number:3; + _BIT_ major:5; +} _VERSION_FIRST_BITS_; + +typedef union +{ + unsigned char Byte; + _VERSION_FIRST_BITS_ bits; +} _VERSION_FIRST_BYTE_; + +typedef struct +{ + _BIT_ local_modification:1; + _BIT_ mixed_update:1; + _BIT_ original_release:1; + _BIT_ ndef:5; +} _REVISION_THIRD_BITS_; + +typedef union +{ + unsigned char Byte; + _REVISION_THIRD_BITS_ bits; +} _REVISION_THIRD_BYTE_; + +typedef struct +{ + _BIT_ no_programming:1; + _BIT_ flash_erased:1; + _BIT_ toc:1; + _BIT_ sounds_track:5; + +} _AUDIO_BITS_; + +typedef union +{ + unsigned char Byte; + _AUDIO_BITS_ bits; +} _AUDIO_BYTE_; + +typedef struct +{ + _BIT_ left_led_fading:1; + _BIT_ left_led_pulsing:1; + _BIT_ right_led_fading:1; + _BIT_ right_led_pulsing:1; + _BIT_ led_mask:1; + _BIT_ ndef:3; +} _LED_EFFECT_STATUS_BITS_; + +typedef union +{ + unsigned char Byte; + _LED_EFFECT_STATUS_BITS_ bits; +} _LED_EFFECT_STATUS_BYTE_; + +typedef struct +{ + _BIT_ spin_right_on:1; + _BIT_ spin_left_on:1; + _BIT_ eyes_on:1; + _BIT_ mouth_on:1; + _BIT_ flippers_on:1; + _BIT_ ndef:3; +} _MOTORS_STATUS_BITS_; + +typedef union +{ + unsigned char Byte; + _MOTORS_STATUS_BITS_ bits; +} _MOTORS_STATUS_BYTE_; + +typedef struct +{ + _PORTB_BYTE_ portb; + _PORTC_BYTE_ portc; + _PORTD_BYTE_ portd; +} frame_body_ports_t; + +typedef struct +{ + _SENSORS_BYTE_ sensors; + unsigned char play_internal_sound; + unsigned char play_general_sound; +} frame_body_sensors1_t; + +typedef struct +{ + unsigned char high_level; + unsigned char low_level; + unsigned char mode; +} frame_body_light_t; + +typedef struct +{ + unsigned char eyes_remaining_mvm; + unsigned char mouth_remaining_mvm; + unsigned char flippers_remaining_mvm; +} frame_body_position1_t; + +typedef struct +{ + unsigned char spin_remaining_mvm; + unsigned char flippers_down; + _MOTORS_STATUS_BYTE_ motors; +} frame_body_position2_t; + +typedef struct +{ + _RC5_BYTE_ rc5_code; + /*unsigned char ??; NDEF */ + /*unsigned char ??; NDEF */ +} frame_body_ir_t; + +typedef struct +{ + unsigned char msb_number; + unsigned char lsb_number; + /*unsigned char ??; NDEF */ +} frame_body_id_t; + +typedef struct +{ + unsigned char high_level; + unsigned char low_level; + unsigned char motors_state; +} frame_body_battery_t; + +typedef struct +{ + _VERSION_FIRST_BYTE_ cm; + unsigned char minor; + unsigned char update; +} frame_body_version_t; + +typedef struct +{ + unsigned char lsb_number; + unsigned char msb_number; + _REVISION_THIRD_BYTE_ release_type; +} frame_body_revision_t; + +typedef struct +{ + unsigned char lsb_id; + unsigned char msb_id; + unsigned char variation_number; +} frame_body_author_t; + +typedef struct +{ + unsigned char sound_track_played; + _AUDIO_BYTE_ programming_steps; + unsigned char programmed_sound_track; +} frame_body_audio_t; + +typedef struct +{ + unsigned char number_of_sounds; + unsigned char flash_usage; + /*unsigned char ??; NDEF */ +} frame_body_sound_var_t; + +typedef struct +{ + unsigned char current_state; + unsigned char last_sound_size; + /*unsigned char ??; NDEF */ +} frame_body_flash_prog_t; + +typedef struct +{ + unsigned char left_led_intensity; + unsigned char right_led_intensity; + _LED_EFFECT_STATUS_BYTE_ effect_status; +} frame_body_led_t; + +typedef struct +{ + unsigned char pongs_pending_number; + unsigned char pongs_lost_by_i2c_number; + unsigned char pongs_lost_by_rf_number; +} frame_body_pong_t; + +typedef struct +{ + frame_body_ports_t ports; + frame_body_sensors1_t sensors1; + frame_body_light_t light; + frame_body_position1_t position1; + frame_body_position2_t position2; + frame_body_ir_t ir; + frame_body_id_t id; + frame_body_battery_t battery; + frame_body_version_t version; + frame_body_revision_t revision; + frame_body_author_t author; + frame_body_audio_t audio; + frame_body_sound_var_t sound_var; + frame_body_flash_prog_t flash_prog; + frame_body_led_t led; + frame_body_pong_t pong; +} hw_status_table_t; + +extern hw_status_table_t hw_status_table; +extern unsigned char tux_hw_status_header_counter[16]; + +extern void tux_hw_status_init(void); +extern int tux_hw_status_parse_frame(const unsigned char *frame); +extern char *tux_hw_status_id_to_str(const unsigned char id); +extern void tux_hw_status_header_counter_check(void); + +extern void tux_hw_parse_body_version(const unsigned char *frame); +extern void tux_hw_parse_body_revision(const unsigned char *frame); +extern void tux_hw_parse_body_author(const unsigned char *frame); + +#endif /* _TUX_HW_STATUS_H_ */ diff --git a/src/tux_id.c b/src/tux_id.c new file mode 100644 index 0000000..3198df3 --- /dev/null +++ b/src/tux_id.c @@ -0,0 +1,89 @@ +/* + * Tux Droid - ID connection + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include + +#include "tux_descriptor.h" +#include "tux_hw_status.h" +#include "tux_hw_cmd.h" +#include "tux_misc.h" +#include "tux_types.h" +#include "tux_usb.h" + +LIBLOCAL id_descriptor_t id_desc; + +/** + * + */ +LIBLOCAL void +tux_id_init_descriptor(void) +{ + memset(&id_desc, 0, sizeof(id_descriptor_t)); +} + +/** + * + */ +LIBLOCAL char * +tux_id_dump_descriptor(char *p) +{ + p = p + sprintf(p, "- ID connection\n - number : \t[%d]\n", id_desc.number); + + return p; +} + +/** + * + */ +LIBLOCAL void +tux_id_update_number(void) +{ + id_desc.number = (hw_status_table.id.msb_number << 8) + + hw_status_table.id.lsb_number; +} + +/** + * + */ +LIBLOCAL void +tux_id_get_descriptor(void) +{ + data_frame frame = + { USB_DONGLE_CONNECTION_CMD, + USB_TUX_CONNECTION_ID_REQUEST, + 0, 0}; + + tux_usb_send_to_dongle(frame); +} + +/** + * + */ +LIBLOCAL bool +tux_id_cmd_disconnect_from_tux(void) +{ + data_frame frame = + { USB_DONGLE_CONNECTION_CMD, + USB_TUX_CONNECTION_DISCONNECT, + 0, 0}; + + return tux_usb_send_to_dongle(frame); +} diff --git a/src/tux_id.h b/src/tux_id.h new file mode 100644 index 0000000..8df1b4c --- /dev/null +++ b/src/tux_id.h @@ -0,0 +1,37 @@ +/* + * Tux Droid - ID + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_ID_H_ +#define _TUX_ID_H_ + +typedef struct +{ + unsigned int number; +} id_descriptor_t; + +extern id_descriptor_t id_desc; + +extern void tux_id_init_descriptor(void); +extern char *tux_id_dump_descriptor(char *descriptor); +extern void tux_id_update_number(void); +extern void tux_id_get_descriptor(void); +extern bool tux_id_cmd_disconnect_from_tux(void); + +#endif /* _TUX_ID_H_ */ diff --git a/src/tux_leds.c b/src/tux_leds.c new file mode 100644 index 0000000..11f123f --- /dev/null +++ b/src/tux_leds.c @@ -0,0 +1,619 @@ +/* + * Tux Droid - Leds + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include +#include + +#include "tux_hw_cmd.h" +#include "tux_hw_status.h" +#include "tux_leds.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" + +typedef enum +{ + LED_ON, + LED_OFF, + LED_CHANGING, +} led_state_t; + +static led_state_t left_led_state = LED_OFF; +static led_state_t right_led_state = LED_OFF; + +static bool led_set(leds_t leds, int intensity, led_effect_t *effect); +static bool led_pulse(leds_t leds, int min_intensity, int max_intensity, + int toggle_count, float pulse_period, led_effect_t *effect); +static int led_configure_effects(leds_t leds, int left_intensity_delta, + int right_intensity_delta, led_effect_t *effect); + +/** + * + */ +static int +bound_to_range(int val, int min, int max) +{ + if (val < min) + { + return(min); + } + if (val > max) + { + return(max); + } + return(val); +} + +/** + * + */ +LIBLOCAL void +tux_leds_update_left(void) +{ + char *new_left_state = ""; + + /* Get on / off state */ + if (hw_status_table.led.left_led_intensity < 50) + { + left_led_state = LED_OFF; + } + else + { + left_led_state = LED_ON; + } + + /* Get changing state */ + if (hw_status_table.led.effect_status.bits.left_led_fading || + hw_status_table.led.effect_status.bits.left_led_pulsing) + { + left_led_state = LED_CHANGING; + } + + /* State to string */ + switch (left_led_state) { + case LED_ON: + new_left_state = STRING_VALUE_ON; + break; + case LED_OFF: + new_left_state = STRING_VALUE_OFF; + break; + case LED_CHANGING: + new_left_state = STRING_VALUE_CHANGING; + break; + } + + tux_sw_status_set_strvalue(SW_ID_LEFT_LED_STATE, new_left_state, true); +} + +/** + * + */ +LIBLOCAL void +tux_leds_update_right(void) +{ + char *new_right_state = ""; + + /* Get on / off state */ + if (hw_status_table.led.right_led_intensity < 50) + { + right_led_state = LED_OFF; + } + else + { + right_led_state = LED_ON; + } + + /* Get changing state */ + if (hw_status_table.led.effect_status.bits.right_led_fading || + hw_status_table.led.effect_status.bits.right_led_pulsing) + { + right_led_state = LED_CHANGING; + } + + /* State to string */ + switch (right_led_state) { + case LED_ON: + new_right_state = STRING_VALUE_ON; + break; + case LED_OFF: + new_right_state = STRING_VALUE_OFF; + break; + case LED_CHANGING: + new_right_state = STRING_VALUE_CHANGING; + break; + } + + tux_sw_status_set_strvalue(SW_ID_RIGHT_LED_STATE, new_right_state, true); +} + +/** Default settings for the fading effect. */ +#define DEFAULT_STEP 1 +/** Default settings for the fading effect. */ +#define DEFAULT_DELAY 1 + +/** Values corresponding to the firmware implementation of the fading effect. + * 'delay' is the delay (4ms time base) before which 'step' is applied. */ +static int delay = DEFAULT_DELAY; +static int step = DEFAULT_STEP; + +/** + * \brief Configure the led effect parameters for a fading effect. + * \param leds Which LEDs should be configured. + * \param fading_delay Delay (in seconds) between 2 increments of intensity + * when fading. + * \return ack of tux command. + * + * One problem is that around 1sec (delay = 1, step = 1, 255 steps to fade from + * off to on), the resolution is quite coarse if we increment step or delay but + * not both simultaneously. We have approximately 0.25s, 0.5s, 1s, 2s, 3s, etc. + * when delay or step is increased. XXX If necessary we can add intermediate + * values by using 3/4 or 2,3,4/5 or even 2,3,7,9/10 and get a 100ms + * resolution, but the gradient (steps) effect starts to be noticeable. + */ +static bool +config_fading(leds_t leds, float fading_delay) +{ + int loops = fading_delay / FW_MAIN_LOOP_DELAY; + data_frame frame = {0, 0, 0, 0}; + + /* Can't go infinitely fast. */ + if (loops == 0) + { + step = 0xFF; + delay = 1; + } + /* XXX We only handle the firmware function for now, + * when the delay will be over 255, we should split in 2 + * commands while tracking the time. */ + else + { + if (loops > 255) + { + step = 1; + delay = 255; + } + /* Faster speed, the delay is set to minimum and we need to + * increase the step. */ + else + { + if (loops < 1) + { + delay = 1; + step = (unsigned char)roundf(1/loops); + } + else + { + /* (loops >= 1) */ + step = 1; + delay = (unsigned char)roundf(loops); + } + } + } + + frame[0] = LED_FADE_SPEED_CMD; + frame[1] = leds; + frame[2] = delay; + frame[3] = step; + + return tux_usb_send_to_tux(frame); +} + +/** + * \brief Configure the led effect parameters for a gradient effect. + * \param leds Which LEDs should be configured. + * \param delta Intensity increment to apply at each step. + * \param gradient_delay Delay (in seconds) between 2 increments of intensity. + * \return ack of tux command. + */ +static int +config_gradient(leds_t leds, int delta, float gradient_delay) +{ + data_frame frame = {0, 0, 0, 0}; + + /* Preconditions. */ + delta = bound_to_range(delta, 1, 255); + + delay = (unsigned char)roundf(gradient_delay / FW_MAIN_LOOP_DELAY); + /* Can't go infinitely fast, so must be > 0. */ + /* Hardware doesn't support longer delays. + * We should do them with multiple commands if necessary. + * XXX Not supported for now. */ + delay = bound_to_range(delay, 1, 255); + + frame[0] = LED_FADE_SPEED_CMD; + frame[1] = leds; + frame[2] = delay; + frame[3] = delta; + + return tux_usb_send_to_tux(frame); +} + +/** + * \brief Configure the hardware to the desired LED effect. + * \param leds Which LEDs are affected by the command + * \param left_intensity_delta Variation of intensity the left LED will have, + * necessary to handle timings. + * \param right_intensity_delta Variation of intensity the right LED will have, + * necessary to handle timings. + * \param effect Fading or gradient effect applied when changing the intensity. + * + * \sa The effect types and parameters are described in the documentation of + * effect_type_t. + */ +static int +led_configure_effects(leds_t leds, int left_intensity_delta, + int right_intensity_delta, led_effect_t *effect) +{ + bool ret = false; + data_frame frame = {0, 0, 0, 0}; + + switch(effect->type) + { + case UNAFFECTED: + /* Just change the intensity in this case. */ + break; + case LAST: + /* Don't update the parameters but send them in case the standalone + * changed them in the meantime. */ + frame[0] = LED_FADE_SPEED_CMD; + frame[1] = leds; + frame[2] = delay; + frame[3] = step; + + ret =tux_usb_send_to_tux(frame); + break; + case NONE: + /* Emulate on/off. */ + step = 0xFF; + delay = 1; + frame[0] = LED_FADE_SPEED_CMD; + frame[1] = leds; + frame[2] = delay; + frame[3] = step; + + ret =tux_usb_send_to_tux(frame); + break; + case DEFAULT: + /* Use default settings. */ + step = DEFAULT_STEP; + delay = DEFAULT_DELAY; + frame[0] = LED_FADE_SPEED_CMD; + frame[1] = leds; + frame[2] = delay; + frame[3] = step; + + ret =tux_usb_send_to_tux(frame); + break; + case FADE_DURATION: + { + /* Intensity is changed by 'step' each 'delay'*4ms. + * So the duration from I1 to I2 is: + * duration = abs(I2 - I1) * 4ms * delay / step + * The fading delay in seconds is + * fading_delay = delay * 4ms / step + * = duration / abs(I2 - I1) + * + * If both leds are not at the same initial level, we can only + * have the same duration by assigning different parameters to + * each eye. */ + /* Flag that indicates if nothing needs to be done. */ + bool skip = true; + + /* We should use effect NONE if we don't want fading. */ + if (effect->speed <= 0) + effect->speed = 0.1; + /* Don't divide by zero if there's nothing to do ;-). */ + if (leds & LED_LEFT && left_intensity_delta) + { + skip = false; + ret = config_fading(LED_LEFT, effect->speed / \ + left_intensity_delta); + } + if (leds & LED_RIGHT && right_intensity_delta) + { + skip = false; + ret = config_fading(LED_RIGHT, effect->speed / \ + right_intensity_delta); + } + if (skip) + { + /* Nothing to do then. */ + return ret; + } + } + break; + case FADE_RATE: + /* Intensity is changed by 'step' each 'delay'*4ms, and it takes + * 255 steps to go from off to on. + * So the duration from off to on is: + * duration = 255 * 4ms * delay / step + * The fading delay in seconds is + * fading_delay = delay * 4ms / step + * = duration / 255 + * Now the delay should be rounded to the closest int or, if + * inferior to 1, we should play with the step in order to + * achieve faster effects. + */ + ret = config_fading(leds, effect->speed / 255); + break; + case GRADIENT_NBR: + { + int delta; + /* Flag indicating if nothing needs to be done. */ + bool skip = true; + + /* Preconditions */ + effect->step = bound_to_range(effect->step, 1, 255); + + /* We should use effect NONE if we don't want gradient. */ + if (effect->speed <= 0) + { + effect->speed = 0.1; + } + + /* Don't divide by zero if there's nothing to do ;-). */ + if (leds & LED_LEFT && left_intensity_delta) + { + skip = false; + delta = (int)roundf(left_intensity_delta / effect->step); + /* If we can't have as many steps as required. */ + if (delta == 0) + { + delta = 1; + } + ret = config_gradient(LED_LEFT, delta, + effect->speed/effect->step); + } + if (leds & LED_RIGHT && right_intensity_delta) + { + skip = false; + delta = (int)roundf(right_intensity_delta / effect->step); + /* If we can't have as many steps as required. */ + if (delta == 0) + { + delta = 1; + } + ret = config_gradient(LED_RIGHT, delta, + effect->speed/effect->step); + } + if (skip) + { + /* Nothing to do then. */ + return ret; + } + } + break; + case GRADIENT_DELTA: + { + float gradient_delay; + /* Flag indicating if nothing needs to be done. */ + bool skip = true; + + /* Preconditions */ + effect->step = bound_to_range(effect->step, 1, 255); + + /* We should use effect NONE if we don't want gradient. */ + if (effect->speed <= 0) + { + effect->speed = 0.1; + } + + /* Don't divide by zero if there's nothing to do ;-). */ + if (leds & LED_LEFT && left_intensity_delta) + { + skip = false; + gradient_delay = effect->speed * effect->step / + left_intensity_delta; + ret = config_gradient(LED_LEFT, effect->step, gradient_delay); + } + if (leds & LED_RIGHT && right_intensity_delta) + { + skip = false; + gradient_delay = effect->speed * effect->step / + right_intensity_delta; + ret = config_gradient(LED_RIGHT, effect->step, gradient_delay); + } + if (skip) + { + /* Nothing to do then. */ + return ret; + } + } + break; + default: + /* Not a correct type. */ + ret = false; + break; + } + return ret; +} + +/** + * \brief Set the intensity of the LEDs. + * \param leds Which LEDs are affected by the command + * \param intensity Value of the intensity the LEDs should be set to. + * \param effect Fading or gradient effect applied when changing the intensity. + * + * \sa The effect types and parameters are described in the documentation of + * effect_type_t. + */ +static bool led_set(leds_t leds, int intensity, led_effect_t *effect) +{ + data_frame frame = {0, 0, 0, 0}; + int left_intensity_delta, right_intensity_delta; + bool ret; + + /* Preconditions */ + if (leds < LED_NONE) + { + leds = LED_NONE; + } + else + { + if (leds > LED_BOTH) + { + leds = LED_BOTH; + } + } + + intensity = bound_to_range(intensity, 0, 255); + + left_intensity_delta = abs(intensity - hw_status_table.led.left_led_intensity); + right_intensity_delta = abs(intensity - hw_status_table.led.right_led_intensity); + ret = led_configure_effects(leds, left_intensity_delta, + right_intensity_delta, effect); + frame[0] = LED_SET_CMD; + frame[1] = leds; + frame[2] = intensity; + + ret =tux_usb_send_to_tux(frame); + return ret; +} + +/** + * \brief Pulse the LEDs. + * \param leds Which LEDs are affected by the command. + * \param min_intensity Value of the minimum intensity when pulsing. + * \param max_intensity Value of the maximum intensity when pulsing. + * \param toggle_count Number of toggles before pusling stops. + * \param pulse_period Period between 2 pulses, in seconds. + * \param effect Fading or gradient effect applied when changing the intensity. + * + * \sa The effect types and parameters are described in the documentation of + * effect_type_t. + * + * The effect duration has priority on the pulse period. If you set the pulse + * period to 0.2s but the fading effect to 0.5s, then you will have 2 effects + * per period (or per pulse) and the pulse period will spend 1s and not 0.2s. + */ +static bool +led_pulse(leds_t leds, int min_intensity, int max_intensity, + int toggle_count, float pulse_period, led_effect_t *effect) +{ + data_frame frame = {0, 0, 0, 0}; + bool ret; + /* Pulse width or duration of the pulse, in hardware loops. The pulse + * period is twice that number. */ + int pulse_width; + int delta; + + /* Preconditions */ + if (leds < LED_NONE) + { + leds = LED_NONE; + } + else + { + if (leds > LED_BOTH) + { + leds = LED_BOTH; + } + } + + min_intensity = bound_to_range(min_intensity, 0, 255); + max_intensity = bound_to_range(max_intensity, 0, 255); + + if (min_intensity > max_intensity) + { + min_intensity = max_intensity; + } + + /* TODO right now the limitation is the firmware limitation of 255, if we + * want to overcome this limitation, this libary should split the user + * command into multiple commands sent over time in order to achieve the + * required effect. i.e. 500 toggles could be split into 2 commands of + * 200 toggles and one of 100, each command sent when the previous one is + * completed. */ + toggle_count = bound_to_range(toggle_count, 1, 255); + + pulse_width = (int)roundf(pulse_period/FW_MAIN_LOOP_DELAY/2); + + /* TODO right now the limitation is the firmware limitation of 255, if we + * want to overcome this limitation, this libary should split the user + * command into multiple commands sent over time in order to achieve the + * required effect. i.e. 500 could be split into 2 commands of + * 200 and one of 100, each command sent when the previous one is + * completed. */ + pulse_width = bound_to_range(pulse_width, 1, 255); + + delta = max_intensity - min_intensity; + ret = led_configure_effects(leds, delta, delta, effect); + + frame[0] = LED_PULSE_RANGE_CMD; + frame[1] = leds; + frame[2] = max_intensity; + frame[3] = min_intensity; + + ret =tux_usb_send_to_tux(frame); + + frame[0] = LED_PULSE_CMD; + frame[1] = leds; + frame[2] = toggle_count; + frame[3] = pulse_width; + + ret = tux_usb_send_to_tux(frame); + return ret; +} + +/** + * + */ +LIBLOCAL bool +tux_leds_cmd_set(leds_t leds, float intensity, unsigned char effect_type, + float effect_speed, unsigned char effect_step) +{ + led_effect_t effect; + + unsigned char intsty2 = 255; + + effect.type = effect_type; + effect.speed = effect_speed; + effect.step = effect_step; + + intsty2 = bound_to_range((int)(255 * intensity), 0, 255); + + return led_set(leds, intsty2, &effect); +} + +/** + * + */ +LIBLOCAL bool +tux_leds_cmd_pulse(leds_t leds, float min_intensity, float max_intensity, + unsigned char pulse_count, float pulse_period, + unsigned char effect_type, float effect_speed, + unsigned char effect_step) +{ + led_effect_t effect; + + unsigned char min_intsty2 = 255; + unsigned char max_intsty2 = 255; + + effect.type = effect_type; + effect.speed = effect_speed; + effect.step = effect_step; + + min_intsty2 = bound_to_range((int)(255 * min_intensity), 0, 255); + max_intsty2 = bound_to_range((int)(255 * max_intensity), 0, 255); + + return led_pulse(leds, min_intsty2, max_intsty2, pulse_count, + pulse_period, &effect); +} diff --git a/src/tux_leds.h b/src/tux_leds.h new file mode 100644 index 0000000..0a85cd7 --- /dev/null +++ b/src/tux_leds.h @@ -0,0 +1,95 @@ +/* + * Tux Droid - Leds + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_LEDS_H_ +#define _TUX_LEDS_H_ + +#include + +/* + * Structure definitions, constants and custom types. + */ + +/** + * Type indicating which led should be affected by the command. + * The left LED is affected to bit0 and the right LED is at bit1. This + * simplifies comparisons. Assigning an hex value helps keep in mind the bit + * relation. + */ +typedef enum +{ + LED_NONE = 0, + LED_LEFT = 0x01, + LED_RIGHT = 0x02, + LED_BOTH = 0x03, +} leds_t; + +/** Types of effects applied when changing the intensity of the LEDs. */ +typedef enum +{ + UNAFFECTED, /**< Don't update the effect parameters. This can either be + the last effect set by software, or by firmware in the + autonomous mode. This is probably not what you want. */ + LAST, /**< Last effect requested by software. */ + NONE, /**< Don't use effects, equivalent to on/off mode. */ + DEFAULT, /**< Default effect which is a short fading effect. */ + FADE_DURATION, /**< Fading effect, 'effect.speed' sets the duration (in + seconds) the effect will last. */ + FADE_RATE, /**< Fading effect, 'effect.speed' sets the rate of the + effect. Its value represents the number of seconds it + takes to apply the effect from off to on. So the actual + effect duration will take less time than specified if the + intensity starts or ends at intermediate values. + Therefore this parameter guarantees a constant rate of + the effect, not the duration. + */ + GRADIENT_NBR, /**< Gradient effect, the intensity changes gradually by a + number of steps given by 'effect.step'. 'effect.speed' + represents the number of seconds it should take to apply + the effect. */ + GRADIENT_DELTA, /**< Gradient effect, the intensity changes by a delta + value of 'effect.step'. 'effect.speed' represents the + number of seconds it should take to apply the effect. */ +} effect_type_t; + +/** Fading or gradient effect. This structure holds the type of effect and the + * corresponding parameters. + * \sa The effect types and parameters are described in the documentation of + * effect_type_t. */ +typedef struct +{ + effect_type_t type; /**< Type of effect. */ + float speed; /**< Speed of the effect, used in both + gradients and fading effects. */ + int step; /**< Intensity step of the gradient effect. Not + used for the fading effect. */ +} led_effect_t; + +extern void tux_leds_update_left(void); +extern void tux_leds_update_right(void); +extern bool tux_leds_cmd_set(leds_t leds, float intensity, + unsigned char effect_type, float effect_speed, + unsigned char effect_step); +extern bool tux_leds_cmd_pulse(leds_t leds, float min_intensity, + float max_intensity, unsigned char pulse_count, float pulse_period, + unsigned char effect_type, float effect_speed, + unsigned char effect_step); + +#endif /* _TUX_LEDS_H_ */ diff --git a/src/tux_light.c b/src/tux_light.c new file mode 100644 index 0000000..04b021b --- /dev/null +++ b/src/tux_light.c @@ -0,0 +1,62 @@ +/* + * Tux Droid - Light + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include + +#include "tux_hw_status.h" +#include "tux_light.h" +#include "tux_misc.h" +#include "tux_sw_status.h" + +static float last_level_for_event = 0.0; + +/** + * + */ +LIBLOCAL void +tux_light_update_level(void) +{ + float new_level = 0.0; + + int light_value; + + light_value = (hw_status_table.light.high_level << 8); + light_value += hw_status_table.light.low_level; + + if (hw_status_table.light.mode == 0) + { + light_value = light_value / 8 + 1000; + } + + light_value = 1128 - light_value; + + new_level = (light_value * 100.0) / 1128.0; + + if (fabs(new_level - last_level_for_event) > 1.0) + { + last_level_for_event = new_level; + tux_sw_status_set_floatvalue(SW_ID_LIGHT_LEVEL, new_level, true); + } + else + { + tux_sw_status_set_floatvalue(SW_ID_LIGHT_LEVEL, new_level, false); + } +} diff --git a/src/tux_light.h b/src/tux_light.h new file mode 100644 index 0000000..cb95e9f --- /dev/null +++ b/src/tux_light.h @@ -0,0 +1,26 @@ +/* + * Tux Droid - Light + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_LIGHT_H_ +#define _TUX_LIGHT_H_ + +extern void tux_light_update_level(void); + +#endif /* _TUX_LIGHT_H_ */ diff --git a/src/tux_misc.c b/src/tux_misc.c new file mode 100644 index 0000000..f294fda --- /dev/null +++ b/src/tux_misc.c @@ -0,0 +1,210 @@ +/* + * Tux Droid - Misc + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include + +#include "tux_misc.h" + +#ifdef WIN32 +# include +# include +#else +# include +#endif + +#ifdef WIN32 +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) +# define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else +# define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +/** + * + */ +static int +gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tmpres /= 10; /*convert into microseconds*/ + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} +#endif /* WIN32 */ + +/** + * + */ +LIBEXPORT double +get_time(void) +{ + double result; + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + result = ((double)tv.tv_usec / 1000000) + (double)tv.tv_sec; + + return result; +} + +LIBLOCAL bool +str_to_uint8(const char *str, unsigned char *dest) +{ + int r, val; + + r = sscanf(str, "%d", &val); + + if (r == 1) + { + if ((val >= 0) && (val <= 255)) + { + *dest = val; + return true; + } + } + + return false; +} + +LIBLOCAL bool +str_to_int8(const char *str, char *dest) +{ + int r, val; + + r = sscanf(str, "%d", &val); + + if (r == 1) + { + if ((val >= -128) && (val <= 127)) + { + *dest = val; + return true; + } + } + + return false; +} + +LIBLOCAL bool +str_to_int(const char *str, int *dest) +{ + int r, val; + + r = sscanf(str, "%d", &val); + + if (r == 1) + { + *dest = val; + return true; + } + + return false; +} + +LIBLOCAL bool +str_to_bool(const char *str, bool *dest) +{ + if (!strcmp(str, "True")) + { + *dest = true; + return true; + } + else + { + if (!strcmp(str, "False")) + { + *dest = false; + return true; + } + } + + return false; +} + +LIBLOCAL bool +str_to_float(const char *str, float *dest) +{ + int r; + float val; + + r = sscanf(str, "%f", &val); + + if (r == 1) + { + *dest = val; + return true; + } + + return false; +} + +LIBLOCAL bool +hex_to_uint8(const char *str, unsigned char *dest) +{ + int r; + int val; + + r = sscanf(str, "0x%2x", &val); + + if (r == 1) + { + if ((val >= 0) && (val <= 255)) + { + *dest = val; + return true; + } + } + + return false; +} diff --git a/src/tux_misc.h b/src/tux_misc.h new file mode 100644 index 0000000..037a2d2 --- /dev/null +++ b/src/tux_misc.h @@ -0,0 +1,55 @@ +/* + * Tux Droid - Misc + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_MISC_H_ +#define _TUX_MISC_H_ + +#include +#include + +#define FW_MAIN_LOOP_DELAY 0.004 + +#ifdef WIN32 +# include +# include +# define sleep(sec) Sleep(sec * 1000) +# define usleep(usec) Sleep(usec / 1000) +# define LIBEXPORT __declspec(dllexport) +# define LIBLOCAL +#else +# define LIBEXPORT __attribute__ ((visibility ("default"))) +# define LIBLOCAL __attribute__ ((visibility ("hidden"))) +#endif + + +/** + * Callback function prototype for simple event + */ +typedef void(*simple_callback_t)(void); + +extern double get_time(void); +extern bool str_to_uint8(const char *str, unsigned char *dest); +extern bool str_to_int8(const char *str, char *dest); +extern bool str_to_int(const char *str, int *dest); +extern bool str_to_bool(const char *str, bool *dest); +extern bool str_to_float(const char *str, float *dest); +extern bool hex_to_uint8(const char *str, unsigned char *dest); + +#endif /* _TUX_MISC_H_ */ diff --git a/src/tux_mouth.c b/src/tux_mouth.c new file mode 100644 index 0000000..5e3cb98 --- /dev/null +++ b/src/tux_mouth.c @@ -0,0 +1,193 @@ +/* + * Tux Droid - Mouth + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_mouth.c + * \brief Mouth functions. + * \author remi.jocaille@c2me.be + * \ingroup mouth + */ + +#include + +#include "tux_cmd_parser.h" +#include "tux_hw_status.h" +#include "tux_hw_cmd.h" +#include "tux_misc.h" +#include "tux_mouth.h" +#include "tux_movements.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" + +/** Counter of mouth movements */ +static unsigned char mvmt_counter = 0; + +/** + * \brief Update the status of the position of the mouth. + */ +LIBLOCAL void +tux_mouth_update_position(void) +{ + char *new_position = ""; + + if (!hw_status_table.ports.portb.bits.mouth_open_switch) + { + new_position = STRING_VALUE_OPEN; + } + else + { + if (!hw_status_table.ports.portb.bits.mouth_closed_switch) + { + new_position = STRING_VALUE_CLOSE; + } + else + { + new_position = STRING_VALUE_NDEF; + } + } + + tux_sw_status_set_strvalue(SW_ID_MOUTH_POSITION, new_position, true); +} + +/** + * \brief Update the status of the motor state of the mouth. + */ +LIBLOCAL void +tux_mouth_update_motor(void) +{ + unsigned char new_state; + + new_state = hw_status_table.position2.motors.bits.mouth_on; + tux_sw_status_set_intvalue(SW_ID_MOUTH_MOTOR_ON, new_state, true); +} + +/** + * \brief Update the status of the mouth movements remaining. + */ +LIBLOCAL void +tux_mouth_update_movements_remaining(void) +{ + unsigned char new_count; + + new_count = hw_status_table.position1.mouth_remaining_mvm; + + mvmt_counter = new_count; + tux_sw_status_set_intvalue(SW_ID_MOUTH_REMAINING_MVM, new_count, true); +} + +/** + * \brief Execute a mouth on command. + * \param counter Number of mouth movements. + * \param final_state Desired final state of the mouth. + * \return The command success result. + */ +LIBLOCAL bool +tux_mouth_cmd_on(unsigned char counter, unsigned char final_state) +{ + return tux_movement_cmd_on(MOVE_MOUTH, counter, final_state); +} + +#include +/** + * \brief Execute a mouth on command. + * \param timeout Duration of the movement. + * \param final_state Desired final state of the mouth. + * \return The command success result. + */ +LIBLOCAL bool +tux_mouth_cmd_on_during(float timeout, unsigned char final_state) +{ + bool ret; + data_frame frame = {MOUTH_MOVE_CMD, 0, 0, 0}; + delay_cmd_t cmd = { 0.0, TUX_CMD, MOUTH }; + + /* Short movements */ + if (timeout < 0.3) + { + return tux_movement_perform(MOVE_MOUTH, 0, timeout, 5, final_state, + false); + } + + /* Long movements */ + ret = tux_usb_send_to_tux(frame); + if (!ret) + { + return false; + } + + mvmt_counter = 255; + tux_sw_status_set_intvalue(SW_ID_MOUTH_REMAINING_MVM, mvmt_counter, true); + + switch (final_state) { + case FINAL_ST_UNDEFINED: + cmd.sub_command = OFF; + break; + case FINAL_ST_OPEN_UP: + cmd.sub_command = OPEN; + break; + case FINAL_ST_CLOSE_DOWN: + cmd.sub_command = CLOSE; + break; + case FINAL_ST_STOP: + cmd.sub_command = OFF; + break; + } + ret = tux_cmd_parser_insert_sys_command(timeout, &cmd); + + return ret; +} + +/** + * \brief Open the mouth. + * \return The command success result. + */ +LIBLOCAL bool +tux_mouth_cmd_open(void) +{ + return tux_movement_perform(MOVE_MOUTH, 0, 0, 5, FINAL_ST_OPEN_UP, false); +} + +/** + * \brief Close the mouth. + * \return The command success result. + */ +LIBLOCAL bool +tux_mouth_cmd_close(void) +{ + return tux_movement_perform(MOVE_MOUTH, 0, 0, 5, FINAL_ST_CLOSE_DOWN, false); +} + +/** + * \brief Stop the mouth movement. + * \return The command success result. + */ +LIBLOCAL bool +tux_mouth_cmd_off(void) +{ + bool ret; + + tux_cmd_parser_clean_sys_command(MOUTH); + ret = tux_movement_perform(MOVE_MOUTH, 0, 0, 5, FINAL_ST_STOP, false); + mvmt_counter = 0; + tux_sw_status_set_intvalue(SW_ID_MOUTH_REMAINING_MVM, mvmt_counter, true); + + return ret; +} diff --git a/src/tux_mouth.h b/src/tux_mouth.h new file mode 100644 index 0000000..9ea2836 --- /dev/null +++ b/src/tux_mouth.h @@ -0,0 +1,49 @@ +/* + * Tux Droid - Mouth + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_mouth.h + * \brief Mouth header. + * \author remi.jocaille@c2me.be + * \ingroup mouth + */ + +#ifndef _TUX_MOUTH_H_ +#define _TUX_MOUTH_H_ + +#include + +/** \brief Mouth position not defined */ +#define TUX_MOUTH_POSITION_NDEF 0 +/** \brief Mouth position open */ +#define TUX_MOUTH_POSITION_OPEN 1 +/** \brief Mouth position close */ +#define TUX_MOUTH_POSITION_CLOSE 2 + +extern void tux_mouth_update_position(void); +extern void tux_mouth_update_motor(void); +extern void tux_mouth_update_movements_remaining(void); +extern bool tux_mouth_cmd_on(unsigned char counter, unsigned char final_state); +extern bool tux_mouth_cmd_on_during(float timeout, unsigned char final_state); +extern bool tux_mouth_cmd_open(void); +extern bool tux_mouth_cmd_close(void); +extern bool tux_mouth_cmd_off(void); + +#endif /* _TUX_MOUTH_H_ */ diff --git a/src/tux_movements.c b/src/tux_movements.c new file mode 100644 index 0000000..cd76a5a --- /dev/null +++ b/src/tux_movements.c @@ -0,0 +1,411 @@ +/* + * Tux Droid - Movements + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include + +#include "tux_hw_cmd.h" +#include "tux_hw_status.h" +#include "tux_movements.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" + +/* + * Prototypes + */ +static int movement_status_control(char final_state, int value, char condition); +static int control_final_state(char motor, char final_state, int value); +static int compute_value(char motor, float timeout); +static void single_movement(char motor, char final_state); + + +/** + * \brief Convert the timeout on a number of movements. + * \param motor Which motor should be configured. + * \param timeout The time (sec.) to convert. + * \return The number of movement to execute. + * + * The firmware use a timebase at 4ms, and is configured with a 8bit + * variable. + * + * The maximum timeout for the firmware is 4ms * 256. When the timeout + * exceed 1s, the time is converted on a number of movements. + * + * The time of each movement has been measured, and defined in movements.h. + */ +static int +compute_value(char motor, float timeout) +{ + int value = 0; + switch (motor) + { + case (MOVE_EYES): + value = (int)(timeout / EYES_MOVE_TIME); + break; + case (MOVE_MOUTH): + value = (int)(timeout / MOUTH_MOVE_TIME); + break; + case (MOVE_FLIPPERS): + value = (int)(timeout / FLIPPERS_MOVE_TIME); + break; + case (MOVE_SPIN_L): + value = (int)(timeout / SPIN_MOVE_TIME); + break; + case (MOVE_SPIN_R): + value = (int)(timeout / SPIN_MOVE_TIME); + break; + } + + if (value > 255) + { + value = 255; + } + return value; +} + +/** + * \brief Control the final state. + * \param motor Which motor should be configured. + * \param final_state The wanted final state. + * \param value The actual number of movements value. + * \return The number of movements with the final_state. + * + * This function verify if the number of mouvements is OK to reach the desired + * final state. + * + * To do this, the actual motor state is analyzed to determine if the 'value' + * variable must be incremented or not. + * + * If the value is null and a final_state has been determined, a single movement + * is executed. + */ +int static +control_final_state(char motor, char final_state, int value) +{ + int condition; + switch (motor) + { + case (MOVE_EYES): + { + condition = hw_status_table.ports.portd.bits.eyes_open_switch; + if (value) + { + value = movement_status_control(final_state, value, condition); + } + else + { + if (final_state != FINAL_ST_UNDEFINED) + { + single_movement(motor, final_state); + } + } + break; + } + case (MOVE_MOUTH): + { + condition = hw_status_table.ports.portb.bits.mouth_open_switch; + if (value) + { + value = movement_status_control(final_state, value, condition); + } + else + { + if (final_state != FINAL_ST_UNDEFINED) + { + single_movement(motor, final_state); + } + } + break; + } + case (MOVE_FLIPPERS): + { + condition = !hw_status_table.position2.flippers_down; + if (value) + { + value = movement_status_control(final_state, value, condition); + } + else + { + if (final_state != FINAL_ST_UNDEFINED) + { + single_movement(motor, final_state); + } + } + break; + } + case (MOVE_SPIN_R): + { + if (!(value) && final_state == FINAL_ST_STOP) + { + single_movement(motor, final_state); + } + break; + } + case (MOVE_SPIN_L): + { + if (!(value) && final_state == FINAL_ST_STOP) + { + single_movement(motor, final_state); + } + break; + } + } + return value; +} + +/** + * \brief Control the number of mouvements. + * \param final_state The wanted final state. + * \param value The actual number of movements. + * \param condition Status to determine the actual state. + * + * This function determines if the number of movements must be incremented or + * not. + * + * The 'condition' parameter must be equal to 1 when the position is + * closed or lower. + */ +int static +movement_status_control(char final_state, int value, char condition) +{ + if ((condition == 2 && final_state == FINAL_ST_OPEN_UP && !(value % 2)) || + (condition == 1 && final_state == FINAL_ST_CLOSE_DOWN && value % 2) || + (condition == 0 && final_state == FINAL_ST_CLOSE_DOWN && !(value % 2)) || + (condition == 0 && final_state == FINAL_ST_OPEN_UP && value % 2) + ) + { + value ++; + } + return value; +} + +/** + * \brief Determine the command to send. + * \param motor Which motor should be configured. + * \param final_state The wanted final state. + * + * Each single movements has its own command. + * + * This function determine which command must to be sent to Tux. + */ +LIBLOCAL void +single_movement(char motor, char final_state) +{ + data_frame frame = {0, 0, 0, 0}; + + switch (motor) + { + case (MOVE_EYES): + if (final_state == FINAL_ST_OPEN_UP) + { + frame[0] = EYES_OPEN_CMD; + } + else + { + if (final_state == FINAL_ST_CLOSE_DOWN) + { + frame[0] = EYES_CLOSE_CMD; + } + else + { + if (final_state == FINAL_ST_STOP) + { + frame[0] = EYES_STOP_CMD; + } + } + } + break; + + case (MOVE_MOUTH): + if (final_state == FINAL_ST_OPEN_UP) + { + frame[0] = MOUTH_OPEN_CMD; + } + else + { + if (final_state == FINAL_ST_CLOSE_DOWN) + { + frame[0] = MOUTH_CLOSE_CMD; + } + else + { + if (final_state == FINAL_ST_STOP) + { + frame[0] = MOUTH_STOP_CMD; + } + } + } + break; + + case (MOVE_FLIPPERS): + if (final_state == FINAL_ST_OPEN_UP) + { + frame[0] = FLIPPERS_RAISE_CMD; + } + else + { + if (final_state == FINAL_ST_CLOSE_DOWN) + { + frame[0] = FLIPPERS_LOWER_CMD; + } + else + { + if (final_state == FINAL_ST_STOP) + { + frame[0] = FLIPPERS_STOP_CMD; + } + } + } + break; + case (MOVE_SPIN_L): + if (final_state == FINAL_ST_STOP) + { + frame[0] = SPIN_STOP_CMD; + } + break; + case (MOVE_SPIN_R): + if (final_state == FINAL_ST_STOP) + { + frame[0] = SPIN_STOP_CMD; + } + break; + default: + return; + } + + tux_usb_send_to_tux(frame); +} + +LIBLOCAL bool +tux_movement_cmd_on(move_body_part_t movement, unsigned char counter, + unsigned char final_state) +{ + return tux_movement_perform(movement, counter, 0.0, 5, final_state, false); +} + +/** + * \brief Control et configure the movements. + * \param motor Which motor should be configured + * \param counter The number of movements to execute + * \param timeout The duration of the movement (if counter = 0) + * \param speed The PWM value. Only applicable for the flippers and the spinning. + * \param final_state The final state to reach + * \param refresh Flag indicate if the entire command must be sent, or just the + * new PWM value. + * \return ack of tux command. + * + * This function analyzes the command received from the API. + * - 'Refresh' has the maximum priority. If this flag is set, only the command + * to change the PWM value is sent. + * - Timeout has priority on counter. + * - If timeout's value is defined, the movement will be executed for the + * specified time. + * - If timeout is lower than 300ms, the timeout will be controlled by the + * Tux's firmwares. In this case, the final state will be ignored. + * - Else, if timeout's value is greater than 300ms, the value is converted on + * a number of movements, and the final state is considered. + * - If 'counter' has been specified, a command is sent to Tux with this value. + * - To do an infinite movement, counter's and timeout's values must be null, + * and final_state must be undefined (0). + * - If 'counter' is equal to 0 and a final state has been specified, a single + * movement is executed if needed.* + */ +LIBLOCAL bool +tux_movement_perform(move_body_part_t movement, unsigned char counter, + float timeout, move_speed_t speed, + move_final_state_t final_state, bool refresh) +{ + data_frame frame = {0, 0, 0, 0}; + int value; + bool ret = false; + char type = 0; + + if (movement == (MOVE_SPIN_L) || movement == (MOVE_SPIN_R)) + { + if (final_state != FINAL_ST_STOP) + { + final_state = FINAL_ST_UNDEFINED; + } + } + + /* + * Refresh has the maximum priority. If it's set, only the command to + * refresh the PWM value is sent to Tux + */ + if (refresh) + { + frame[0] = MOTORS_CONFIG_CMD; + frame[1] = movement; + frame[2] = speed; + ret = tux_usb_send_to_tux(frame); + } + else + { + /* + * Timeout has priority on counter. + */ + if (timeout == 0) + { + value = counter; + value = control_final_state(movement, final_state, value); + } + else + { + /* + * If timeout is lower than 300ms, the timeout value is sent to Tux + */ + if (timeout < 0.3) + { + type = 1; + value = timeout / 0.004; + } + /* + * Else, the timeout's value is converted on a number of movements. + * The final state is considered + */ + else + { + value = compute_value(movement, timeout); + value = control_final_state(movement, final_state, value); + } + } + /* + * If a normal movement must be sent, the value is not null, or if value + * is null, the final state must be also null (infinite movement). + * + * If the value is null and a final state has been specified, this means + * that a single move has already been sent. No command must be sent. + */ + if (value || !(final_state)) + { + frame[0] = MOTORS_CONFIG_CMD; + frame[1] = movement; + frame[2] = speed; + ret = tux_usb_send_to_tux(frame); + + frame[0] = MOTORS_SET_CMD; + frame[1] = movement; + frame[2] = value; + frame[3] = type; + ret = tux_usb_send_to_tux(frame); + } + } + return ret; +} diff --git a/src/tux_movements.h b/src/tux_movements.h new file mode 100644 index 0000000..6d71a9e --- /dev/null +++ b/src/tux_movements.h @@ -0,0 +1,63 @@ +/* + * Tux Droid - Movements + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_MOVEMENTS_H_ +#define _TUX_MOVEMENTS_H_ + +#include + +#define EYES_MOVE_TIME 0.2 +#define MOUTH_MOVE_TIME 0.25 +#define FLIPPERS_MOVE_TIME 0.3 +#define SPIN_MOVE_TIME 0.2 + +typedef enum +{ + MOVE_EYES = 0, + MOVE_MOUTH = 1, + MOVE_FLIPPERS = 2, + MOVE_SPIN_R = 3, + MOVE_SPIN_L = 4, +} move_body_part_t; + +typedef enum +{ + SPEED_VERYLOW = 1, + SPEED_LOW = 2, + SPEED_MEDIUM = 3, + SPEED_MIDHIGH = 4, + SPEED_HIGH = 5, +} move_speed_t; + +typedef enum +{ + FINAL_ST_UNDEFINED = 0, + FINAL_ST_OPEN_UP = 1, + FINAL_ST_CLOSE_DOWN = 2, + FINAL_ST_STOP = 3, +} move_final_state_t; + +extern bool tux_movement_cmd_on(move_body_part_t movement, + unsigned char counter, unsigned char final_state); +extern bool tux_movement_perform(move_body_part_t movement, + unsigned char counter, float timeout, move_speed_t speed, + move_final_state_t final_state, bool refresh); + +#endif /* _TUX_MOVEMENTS_H_ */ diff --git a/src/tux_pong.c b/src/tux_pong.c new file mode 100644 index 0000000..0819df6 --- /dev/null +++ b/src/tux_pong.c @@ -0,0 +1,112 @@ +/* + * Tux Droid - Pong + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include "tux_hw_status.h" +#include "tux_hw_cmd.h" +#include "tux_pong.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" + +#define STACK_SIZE 10 + +static int average_stack[STACK_SIZE]; +static unsigned char stack_idx = 0; +static unsigned char stack_fill_count = 0; + +/** + * + */ +static void +stack_insert(int value) +{ + if (value > 100) + { + value = 100; + } + + average_stack[stack_idx] = value; + stack_idx++; + + if (stack_fill_count < STACK_SIZE) + { + stack_fill_count++; + } + + stack_idx %= STACK_SIZE; +} + +/** + * + */ +static int +stack_average(void) +{ + unsigned char i; + int average = 0; + + for (i = 0; i < stack_fill_count; i++) + { + average += average_stack[i]; + } + + return (average / stack_fill_count); +} + +/** + * + */ +LIBLOCAL void +tux_pong_update(void) +{ + static unsigned char received_pong = 0; + int average; + + received_pong++; + + if (hw_status_table.pong.pongs_pending_number <= 190) + { + if (received_pong > 1) + { + stack_insert(received_pong * 10); + average = stack_average(); + tux_sw_status_set_intvalue(SW_ID_CONNECTION_QUALITY, average, true); + } + received_pong = 0; + } +} + +/** + * + */ +LIBLOCAL void +tux_pong_get(void) +{ + static unsigned char get_count = 0; + data_frame frame = { TUX_PONG_PING_CMD, 200, 0, 0}; + + get_count++; + + if (get_count >= 40) + { + get_count = 0; + tux_usb_send_to_tux(frame); + } +} diff --git a/src/tux_pong.h b/src/tux_pong.h new file mode 100644 index 0000000..ce65382 --- /dev/null +++ b/src/tux_pong.h @@ -0,0 +1,27 @@ +/* + * Tux Droid - Pong + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_PONG_H_ +#define _TUX_PONG_H_ + +extern void tux_pong_update(void); +extern void tux_pong_get(void); + +#endif /* _TUX_PONG_H_ */ diff --git a/src/tux_sound_flash.c b/src/tux_sound_flash.c new file mode 100644 index 0000000..fd9b67e --- /dev/null +++ b/src/tux_sound_flash.c @@ -0,0 +1,778 @@ +/* + * Tux Droid - Sound flash + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include +#include +#include + +#include "log.h" +#include "tux_cmd_parser.h" +#include "tux_hw_status.h" +#include "tux_hw_cmd.h" +#include "tux_leds.h" +#include "tux_sw_status.h" +#include "tux_sound_flash.h" +#include "tux_types.h" +#include "tux_usb.h" + +LIBLOCAL sound_flash_descriptor_t sound_flash_desc; + +LIBLOCAL char knowed_track_num[128] = "0"; + +#ifdef unix + + /* Patch pour les processeurs 64 bits */ + #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) + LIBLOCAL char hw_audio_name[12] = ""; + #else + LIBLOCAL char hw_audio_name[7] = ""; + #endif + +static void get_hw_audio_device_name(void); +#endif + +typedef enum { + SRS_STDBY, + SRS_INIT, + SRS_ERASE, + SRS_WRITE, + SRS_FINISH, +} sound_reflash_state_t; + +typedef enum { + SRE_NO_ERROR, + SRE_RF_OFFLINE, + SRE_WAV_ERROR, + SRE_USB_ERROR, +} sound_reflash_errors_t; + +typedef struct { + tokens_t wav_path; + unsigned char wav_count; + unsigned char current_wav; + unsigned long full_size; + unsigned long wav_size[256]; + sound_reflash_errors_t error; + sound_reflash_state_t current_state; +} sound_reflash_info_t; + +static sound_reflash_info_t reflash_info; + +static void load_knowed_track_num(void); +static void init_reflash_info(void); +static bool play_wav(const char *wav_path); + +/** + * Init the sound flash descriptor part. + */ +LIBLOCAL void +tux_sound_flash_init_descriptor(void) +{ + memset(&sound_flash_desc, 0, sizeof(sound_flash_descriptor_t)); + load_knowed_track_num(); +#ifdef unix + hw_audio_name[0] = '\0'; + get_hw_audio_device_name(); +#endif +} + +/** + * Dump the sound flash descriptor part. + */ +LIBLOCAL char * +tux_sound_flash_dump_descriptor(char *p) +{ + p = p + sprintf(p, + "- Sound flash\n" + " Number of sounds : %d\n" + " Last used block : %d\n" + " Available record time (sec) : %d\n", + sound_flash_desc.number_of_sounds, + sound_flash_desc.flash_usage, + sound_flash_desc.available_record_time + ); + + return p; +} + +/** + * Update the sound flash descriptor part with the low level status. + */ +LIBLOCAL void +tux_sound_flash_update(void) +{ + sound_flash_descriptor_t *sf_desc; + + sf_desc = &sound_flash_desc; + sf_desc->number_of_sounds = hw_status_table.sound_var.number_of_sounds; + sf_desc->flash_usage = hw_status_table.sound_var.flash_usage; + sf_desc->available_record_time = (int)((128 - sf_desc->flash_usage) * 0.5); + + tux_sw_status_set_intvalue(SW_ID_FLASH_SOUND_COUNT, sf_desc->number_of_sounds, true); +} + +/** + * Retry the sound flash infos from the dongle. + */ +LIBLOCAL void +tux_sound_flash_get_descripor(void) +{ + /* Currently, the status are sent by tuxaudio when then + * the versionning is requested */ +} + +/** + * Update the flash play state with the low level status. + */ +LIBLOCAL void +tux_sound_flash_update_flash_play(void) +{ + char *new_track = ""; + static char track[2][10]; + static int tracktoggle = 0; + + /* + the thing with track is a little bit wicked, + set_strvalue will compare on pointer so the array must be static + also we need two arrays and toggle between them otherwise we + will not get the event. + */ + tracktoggle = !tracktoggle; + if (!hw_status_table.audio.sound_track_played) + { + new_track = STRING_VALUE_STOP; + } + else + { + sprintf(track[tracktoggle], "TRACK_%.3d", + hw_status_table.audio.sound_track_played); + new_track = track[tracktoggle]; + } + + tux_sw_status_set_strvalue(SW_ID_AUDIO_FLASH_PLAY, new_track, true); +} + +/** + * Update the general sound play with the low level status. + */ +LIBLOCAL void +tux_sound_flash_update_general_play(void) +{ + unsigned char new_state; + + new_state = hw_status_table.sensors1.play_general_sound; + new_state |= hw_status_table.sensors1.play_internal_sound; + + tux_sw_status_set_intvalue(SW_ID_AUDIO_GENERAL_PLAY, new_state, true); +} + +/** + * + */ +LIBLOCAL void +tux_sound_flash_update_prog_current_track(void) +{ + unsigned char new_track; + + new_track = hw_status_table.flash_prog.current_state; + + tux_sw_status_set_intvalue(SW_ID_FLASH_PROG_CURR_TRACK, new_track, true); +} + +/** + * + */ +LIBLOCAL void +tux_sound_flash_update_prog_last_track_size(void) +{ + int new_size; + + new_size = hw_status_table.flash_prog.last_sound_size; + + tux_sw_status_set_intvalue(SW_ID_FLASH_PROG_LAST_TRACK_SIZE, new_size, true); +} + +/** + * Load the sound flash track number from a file. + */ +static void +load_knowed_track_num(void) +{ +#ifdef LOCK_TUX + FILE *f; + char *ret_c; + + f = fopen("./track_num", "r"); + + if (f) + { + fgets(knowed_track_num, sizeof(knowed_track_num)-2, f); + ret_c = strchr(knowed_track_num, '\n'); + *ret_c = '\0'; + fclose(f); + } +#endif +} + +/** + * Store the sound flash track number in a file. + */ +#ifdef LOCK_TUX +static void +save_knowed_track_num(void) +{ + FILE *f; + + f = fopen("./track_num", "w"); + + if (f) + { + fprintf(f, "%d\n", sound_flash_desc.number_of_sounds); + fclose(f); + } +} +#endif + +/** + * Check the new descriptor. + * Determine if the sound flash track number have changed. + */ +LIBLOCAL bool +tux_sound_flash_check_new_descriptor(bool save) +{ +#ifdef LOCK_TUX + bool ret = false; + char track_num_str[8] = ""; + + sprintf(track_num_str, "%d", sound_flash_desc.number_of_sounds); + + if (strcmp(knowed_track_num, track_num_str)) + { + ret = true; + } + + if (save) + { + save_knowed_track_num(); + } + + return ret; +#else + return false; +#endif +} + +/** + * Send a command to tuxdroid to play a sound flash. + */ +LIBLOCAL bool +tux_sound_flash_cmd_play(unsigned char track_num, float vol) +{ + data_frame frame = {PLAY_SOUND_CMD, 0, 0, 0}; + unsigned char vol2 = 0; + + if (vol < 0.0) + { + vol2 = 7; + } + else + { + if (vol > 100.0) + { + vol2 = 0; + } + else + { + vol2 = 7 - (int)(vol * 7.0 / 100.0); + } + } + + frame[1] = track_num; + frame[2] = vol2; + + return tux_usb_send_to_tux(frame); +} + +#ifdef WIN32 +#define DRVM_MAPPER 0x2000 +#define DRVM_MAPPER_PREFERRED_SET DRVM_MAPPER + 22 +#define DRVM_MAPPER_PREFERRED_GET DRVM_MAPPER + 21 +/** + * + */ +static bool +set_tux_as_default_playback(void) +{ + int i; + WAVEOUTCAPSA wa_device; + + if (!waveOutGetNumDevs()) + { + return false; + } + + for (i = 0; i < waveOutGetNumDevs(); i++) + { + memset(&wa_device, 0, sizeof(wa_device)); + if (waveOutGetDevCapsA(i, &wa_device, sizeof(wa_device)) == 0) + { + if (strstr(wa_device.szPname, "TuxDroid-Audio") != NULL) + { + if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, + DRVM_MAPPER_PREFERRED_SET, i, 0) == 0) + { + return true; + } + } + } + } + + return false; +} + + +/** + * + */ +static bool +get_default_playback_idx(int *idx) +{ + int err; + DWORD po = -1; + DWORD pf = 0; + + err = waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD)&po, (DWORD)&pf); + if (err == 0) + { + *idx = po; + return true; + } + + return true; +} + +/** + * + */ +static bool +get_default_playback_name(char *name) +{ + WAVEOUTCAPSA wa_device; + int def_idx = -1; + + if (!get_default_playback_idx(&def_idx)) + { + return false; + } + + if (waveOutGetDevCapsA(def_idx, &wa_device, sizeof(wa_device)) == 0) + { + strcpy(name, wa_device.szPname); + return true; + } + + return false; +} + +/** + * + */ +static bool +get_idx_playback_from_name(int *idx, const char *name) +{ + int i; + WAVEOUTCAPSA wa_device; + + if (!waveOutGetNumDevs()) + { + return false; + } + + for (i = 0; i < waveOutGetNumDevs(); i++) + { + memset(&wa_device, 0, sizeof(wa_device)); + if (waveOutGetDevCapsA(i, &wa_device, sizeof(wa_device)) == 0) + { + if (!strcmp(wa_device.szPname, name)) + { + *idx = i; + return true; + } + } + } + + return false; +} + +/** + * + */ +static bool +set_default_playback(int idx) +{ + if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_SET, + idx, 0) == 0) + { + return true; + } + + return false; +} + +/** + * + */ +LIBLOCAL void +tux_sound_flash_avoid_tts_default_sound_card(void) +{ + char def_dev_name[256] = ""; + + get_default_playback_name(def_dev_name); + + if (strstr(def_dev_name, "TuxDroid-TTS") != NULL) + { + set_tux_as_default_playback(); + } +} + +#else /* UNIX */ + +/** + * + */ +static void +get_hw_audio_device_name(void) +{ + FILE *fp; + int card; + char filename[29]; + char line[81]; + unsigned int vid, pid; + + for (card = 0; card < 255; card++) + { + sprintf(filename, "/proc/asound/card%d/usbid", card); + fp = fopen((const char *)filename, "r"); + if(fp != NULL) + { + fgets(line, 81, fp); + sscanf( line, "%4x:%4x", &vid, &pid); + fclose(fp); + if((vid == TUX_VID) && (pid == TUX_PID)) + { + /* Todo log this */ + sprintf(hw_audio_name, "default:%d,0", card); + return; + } + } + } +} +#endif /* WIN32 || UNIX */ + +/** + * + */ +static bool +play_wav(const char *wav_path) +{ + bool ret = true; + +#ifdef WIN32 + char def_dev_name[256] = ""; + int def_idx = -1; + + get_default_playback_name(def_dev_name); + + if (!set_tux_as_default_playback()) + { + return false; + } + + if (sndPlaySound(wav_path, SND_SYNC) != true) + { + ret = false; + } + + if (!get_idx_playback_from_name(&def_idx, def_dev_name)) + { + return false; + } + + if (!set_default_playback(def_idx)) + { + return false; + } + +#else /* UNIX */ + int r; + char cmd[256] = ""; + + ret = false; + + if (hw_audio_name[0] != '\0') + { + sprintf(cmd, "aplay -D plughw:TuxDroid %s", wav_path); + r = system(cmd); + if (r == 0) + ret = true; + } +#endif + + return ret; +} + +/** + * + */ +static void +init_reflash_info(void) +{ + memset(&reflash_info, 0, sizeof(sound_reflash_info_t)); + reflash_info.error = SRE_NO_ERROR; + reflash_info.current_state = SRS_STDBY; +} + +/** + * + */ +static int +parse_wavs(const char *src_str, tokens_t *toks) +{ + return tux_cmd_parser_get_tokens(src_str, toks, 256, "|"); +} + +/** + * + */ +LIBLOCAL TuxDrvError +tux_sound_flash_cmd_reflash(const char *tracks) +{ + FILE *fp; + int i; + int block_count = 0; + int tmp_bc = 0; + + if (reflash_info.current_state != SRS_STDBY) + { + return E_TUXDRV_BUSY; + } + + init_reflash_info(); + reflash_info.wav_count = parse_wavs(tracks, &reflash_info.wav_path); + + if (!reflash_info.wav_count) + { + return E_TUXDRV_BADWAVFILE; + } + + if (reflash_info.wav_count > 0) + { + for (i = 0; i < reflash_info.wav_count; i++) + { + /* open the wav file */ + fp = fopen((char *)reflash_info.wav_path[i], "rb"); + /* if wav_path not accessible then exit */ + if (fp == NULL) + { + return E_TUXDRV_BADWAVFILE; + } + /* Get the size of file */ + fseek(fp, 0, SEEK_END); + reflash_info.wav_size[i] = ftell(fp) - 44; + fclose(fp); + reflash_info.full_size += reflash_info.wav_size[i]; + /* Get the needed blocks number for this sound */ + tmp_bc = (int)(reflash_info.wav_size[i] / 4000); + if ((int)(reflash_info.wav_size[i] % 4000) > 0) + { + tmp_bc++; + } + /* Add to global blocks count */ + block_count += tmp_bc; + } + } + + /* If needed blocks exceeds 127 then fail */ + if (block_count > 127) + { + return E_TUXDRV_WAVSIZEEXCEDED; + } + + reflash_info.current_state = SRS_INIT; + + tux_cmd_parser_clear_delay_commands(); + + return E_TUXDRV_NOERROR; +} + +/** + * + */ +LIBLOCAL void +tux_sound_flash_state_machine_call(void) +{ + bool rf_state = false; + float full_time_sec = 0.0; + data_frame frame = {0, 0, 0, 0}; + unsigned char curr_track_for_event = 0; + + /* Check fux connection and radio connection */ + if (reflash_info.current_state != SRS_STDBY) + { + rf_state = tux_usb_get_rf_state(); + if ((!tux_usb_connected()) || (!rf_state)) + { + reflash_info.error = SRE_RF_OFFLINE; + reflash_info.current_state = SRS_FINISH; + } + } + + switch (reflash_info.current_state) { + case SRS_STDBY: + break; + case SRS_INIT: + /* Disabling commands parsing */ + tux_cmd_parser_set_enable(false); + /* Send begin flashing event with the fulltime of processing */ + full_time_sec = 10.0 + (reflash_info.full_size / 8000.0); + full_time_sec += reflash_info.wav_count * 0.97; + tux_sw_status_set_floatvalue(SW_ID_SOUND_REFLASH_BEGIN, + full_time_sec, true); + tux_sw_status_set_strvalue(SW_ID_SOUND_REFLASH_END, STRING_VALUE_NDEF, + true); + /* Goto SRS_ERASE state */ + reflash_info.current_state = SRS_ERASE; + break; + case SRS_ERASE: + /* Leds pulsing */ + tux_leds_cmd_set(LED_LEFT, 0.0, NONE, 0, 0); + tux_leds_cmd_set(LED_RIGHT, 1.0, NONE, 0, 0); + tux_leds_cmd_pulse(LED_BOTH, 0.0, 1.0, 255, 1.0, + FADE_DURATION, 0.5, 0); + /* Send erase cmd */ + log_info("Sound reflash: Erasing"); + frame[0] = ERASE_FLASH_CMD; + if (!tux_usb_send_to_tux(frame)) + { + reflash_info.error = SRE_USB_ERROR; + reflash_info.current_state = SRS_FINISH; + break; + } + /* Wait that the flash has been erased */ + sleep(10); + /* Set first track to write */ + reflash_info.current_wav = 0; + /* Goto SRS_WRITE state */ + reflash_info.current_state = SRS_WRITE; + break; + case SRS_WRITE: + /* Send store sound command */ + log_info("Sound reflash: Store track (%d of %d)", + reflash_info.current_wav + 1, + reflash_info.wav_count); + frame[0] = STORE_SOUND_CMD; + if (!tux_usb_send_to_tux(frame)) + { + reflash_info.error = SRE_USB_ERROR; + reflash_info.current_state = SRS_FINISH; + break; + } + curr_track_for_event = reflash_info.current_wav + 1; + tux_sw_status_set_intvalue(SW_ID_SOUND_REFLASH_CURRENT_TRACK, + curr_track_for_event, true); + usleep(200000); + /* Play current wav track */ + if (!play_wav(reflash_info.wav_path[reflash_info.current_wav])) + { + frame[0] = CONFIRM_STORAGE_CMD; + frame[1] = 0; + if (!tux_usb_send_to_tux(frame)) + { + reflash_info.error = SRE_USB_ERROR; + reflash_info.current_state = SRS_FINISH; + break; + } + reflash_info.error = SRE_WAV_ERROR; + reflash_info.current_state = SRS_FINISH; + break; + } + /* Send confirm track command */ + usleep(200000); + frame[0] = CONFIRM_STORAGE_CMD; + frame[1] = 1; + if (!tux_usb_send_to_tux(frame)) + { + reflash_info.error = SRE_USB_ERROR; + reflash_info.current_state = SRS_FINISH; + break; + } + usleep(100000); + /* Set next track to write */ + reflash_info.current_wav += 1; + /* If the next track is out of limit, Goto SRS_FINISH state */ + if (reflash_info.current_wav >= reflash_info.wav_count) + { + reflash_info.current_state = SRS_FINISH; + } + break; + case SRS_FINISH: + /* Enabling commands parsing */ + tux_cmd_parser_set_enable(true); + /* Leds stop */ + tux_leds_cmd_set(LED_BOTH, 1.0, NONE, 0, 0); + /* Send end of flashing event */ + full_time_sec = 0.0; + tux_sw_status_set_floatvalue(SW_ID_SOUND_REFLASH_BEGIN, + full_time_sec, false); + switch (reflash_info.error) { + case SRE_NO_ERROR: + tux_sw_status_set_strvalue(SW_ID_SOUND_REFLASH_END, + STRING_VALUE_NO_ERROR, true); + log_info("Sound reflash: Finish (%s)", + STRING_VALUE_NO_ERROR); + /* Play first track */ + tux_sound_flash_cmd_play(1, 100.0); + break; + case SRE_RF_OFFLINE: + tux_sw_status_set_strvalue(SW_ID_SOUND_REFLASH_END, + STRING_VALUE_ERROR_RF_OFFLINE, true); + log_info("Sound reflash: Finish (%s)", + STRING_VALUE_ERROR_RF_OFFLINE); + break; + case SRE_WAV_ERROR: + tux_sw_status_set_strvalue(SW_ID_SOUND_REFLASH_END, + STRING_VALUE_ERROR_WAV_ERROR, true); + log_info("Sound reflash: Finish (%s)", + STRING_VALUE_ERROR_WAV_ERROR); + break; + case SRE_USB_ERROR: + tux_sw_status_set_strvalue(SW_ID_SOUND_REFLASH_END, + STRING_VALUE_ERROR_USB, true); + log_info("Sound reflash: Finish (%s)", + STRING_VALUE_ERROR_USB); + break; + } + /* Goto SRS_STDBY state */ + reflash_info.current_state = SRS_STDBY; + break; + } +} diff --git a/src/tux_sound_flash.h b/src/tux_sound_flash.h new file mode 100644 index 0000000..315e175 --- /dev/null +++ b/src/tux_sound_flash.h @@ -0,0 +1,53 @@ +/* + * Tux Droid - Sound flash + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_SOUND_FLASH_H_ +#define _TUX_SOUND_FLASH_H_ + +#include + +#include "tux_error.h" + +typedef struct +{ + unsigned int number_of_sounds; + unsigned int flash_usage; + unsigned int available_record_time; +} sound_flash_descriptor_t; + +extern sound_flash_descriptor_t sound_flash_desc; + +extern char knowed_track_num[128]; + +extern void tux_sound_flash_init_descriptor(void); +extern char *tux_sound_flash_dump_descriptor(char *descriptor); +extern void tux_sound_flash_update_flash_play(void); +extern void tux_sound_flash_update_general_play(void); +extern void tux_sound_flash_update_prog_current_track(void); +extern void tux_sound_flash_update_prog_last_track_size(void); +extern void tux_sound_flash_update(void); +extern void tux_sound_flash_get_descripor(void); +extern bool tux_sound_flash_check_new_descriptor(bool save); +extern bool tux_sound_flash_cmd_play(unsigned char track_num, float volume); +extern void tux_sound_flash_state_machine_call(void); +extern TuxDrvError tux_sound_flash_cmd_reflash(const char *tracks); +extern void tux_sound_flash_avoid_tts_default_sound_card(void); + +#endif /* _TUX_SOUND_FLASH_H_ */ diff --git a/src/tux_spinning.c b/src/tux_spinning.c new file mode 100644 index 0000000..9daa97c --- /dev/null +++ b/src/tux_spinning.c @@ -0,0 +1,232 @@ +/* + * Tux Droid - Spinning + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_spinning.c + * \brief Spinning functions. + * \author remi.jocaille@c2me.be + * \ingroup spinning + */ + +#include + +#include "tux_cmd_parser.h" +#include "tux_hw_status.h" +#include "tux_hw_cmd.h" +#include "tux_movements.h" +#include "tux_spinning.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" + +/** Counter of spinning movements */ +static unsigned char mvmt_counter = 0; + +/** + * \brief Update the status of the direction of the spinning. + */ +LIBLOCAL void +tux_spinning_update_direction(void) +{ + char *new_direction = ""; + + if ((!hw_status_table.position2.motors.bits.spin_left_on) && + (!hw_status_table.position2.motors.bits.spin_right_on)) + { + new_direction = STRING_VALUE_NONE; + } + else + { + if (hw_status_table.position2.motors.bits.spin_left_on) + { + new_direction = STRING_VALUE_LEFT; + } + else + { + if (hw_status_table.position2.motors.bits.spin_right_on) + { + new_direction = STRING_VALUE_RIGHT; + } + } + } + + tux_sw_status_set_strvalue(SW_ID_SPINNING_DIRECTION, new_direction, true); +} + +/** + * \brief Update the status of the motor state of the left spin. + */ +LIBLOCAL void +tux_spinning_update_left_motor(void) +{ + unsigned char new_state; + + new_state = hw_status_table.position2.motors.bits.spin_left_on; + tux_sw_status_set_intvalue(SW_ID_SPIN_LEFT_MOTOR_ON, new_state, true); +} + +/** + * \brief Update the status of the motor state of the right spin. + */ +LIBLOCAL void +tux_spinning_update_right_motor(void) +{ + unsigned char new_state; + + new_state = hw_status_table.position2.motors.bits.spin_right_on; + tux_sw_status_set_intvalue(SW_ID_SPIN_RIGHT_MOTOR_ON, new_state, true); +} + +/** + * \brief Update the status of the spinning movements remaining. + */ +LIBLOCAL void +tux_spinning_update_movements_remaining(void) +{ + unsigned char new_count; + + new_count = hw_status_table.position2.spin_remaining_mvm; + + mvmt_counter = new_count; + tux_sw_status_set_intvalue(SW_ID_SPINNING_REMAINING_MVM, new_count, true); +} + +/** + * \brief Set the speed of the spinning movements. + * \param speed Movement speed. + * \return The command success result. + */ +LIBLOCAL bool +tux_spinning_cmd_speed(unsigned char speed) +{ + return tux_movement_perform(MOVE_SPIN_R, 0, 0.0, speed, FINAL_ST_UNDEFINED, + true); +} + +/** + * \brief Execute a spinning on command. + * \param movement Direction of the movement + * \param counter Number of spinning movements. + * \return The command success result. + */ +static bool +tux_spinning_cmd_on(move_body_part_t movement, unsigned char counter) +{ + return tux_movement_perform(movement, counter, 0.0, 5, FINAL_ST_UNDEFINED, + false); +} + +/** + * \brief Execute a spinning on command to the left. + * \param counter Number of spinning movements. + * \return The command success result. + */ +LIBLOCAL bool +tux_spinning_cmd_left_on(unsigned char counter) +{ + return tux_spinning_cmd_on(MOVE_SPIN_L, counter); +} + +/** + * \brief Execute a spinning on command to the right. + * \param counter Number of spinning movements. + * \return The command success result. + */ +LIBLOCAL bool +tux_spinning_cmd_right_on(unsigned char counter) +{ + return tux_spinning_cmd_on(MOVE_SPIN_R, counter); +} + +/** + * \brief Execute a spinning on command. + * \param timeout Duration of the movement. + * \param command Command for long movement. + * \param movement Direction of the movement. + * \return The command success result. + */ +static bool +tux_spinning_cmd_on_during(float timeout, unsigned char command, + move_body_part_t movement) +{ + bool ret; + data_frame frame = { command, 0, 5, 0}; + delay_cmd_t cmd = { 0.0, TUX_CMD, SPINNING, OFF }; + + /* Short movements */ + if (timeout < 0.3) + { + return tux_movement_perform(movement, 0, timeout, 5, + FINAL_ST_UNDEFINED, false); + } + + /* Long movements */ + ret = tux_usb_send_to_tux(frame); + if (!ret) + { + return false; + } + + mvmt_counter = 255; + tux_sw_status_set_intvalue(SW_ID_SPINNING_REMAINING_MVM, mvmt_counter, true); + + ret = tux_cmd_parser_insert_sys_command(timeout, &cmd); + + return ret; +} + +/** + * \brief Execute a spinning on command to the left. + * \param timeout Duration of the movement. + * \return The command success result. + */ +LIBLOCAL bool +tux_spinning_cmd_left_on_during(float timeout) +{ + return tux_spinning_cmd_on_during(timeout, SPIN_LEFT_CMD, MOVE_SPIN_L); +} + +/** + * \brief Execute a spinning on command to the right. + * \param timeout Duration of the movement. + * \return The command success result. + */ +LIBLOCAL bool +tux_spinning_cmd_right_on_during(float timeout) +{ + return tux_spinning_cmd_on_during(timeout, SPIN_RIGHT_CMD, MOVE_SPIN_R); +} + +/** + * \brief Stop the spinning movement. + * \return The command success result. + */ +LIBLOCAL bool +tux_spinning_cmd_off(void) +{ + bool ret; + + tux_cmd_parser_clean_sys_command(SPINNING); + ret = tux_movement_perform(MOVE_SPIN_R, 0, 0, 5, FINAL_ST_STOP, false); + mvmt_counter = 0; + tux_sw_status_set_intvalue(SW_ID_SPINNING_REMAINING_MVM, mvmt_counter, true); + + return ret; +} diff --git a/src/tux_spinning.h b/src/tux_spinning.h new file mode 100644 index 0000000..e508ecf --- /dev/null +++ b/src/tux_spinning.h @@ -0,0 +1,52 @@ +/* + * Tux Droid - Spinning + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/** + * \file tux_spinning.h + * \brief Spinning header. + * \author remi.jocaille@c2me.be + * \ingroup spinning + */ + +#ifndef _TUX_SPINNING_H_ +#define _TUX_SPINNING_H_ + +#include + +/** \brief Spinning direction none (stopped) */ +#define TUX_SPINNING_DIRECTION_NONE 0 +/** \brief Spinning direction left */ +#define TUX_SPINNING_DIRECTION_LEFT 1 +/** \brief Spinning direction right */ +#define TUX_SPINNING_DIRECTION_RIGHT 2 + +extern void tux_spinning_update_direction(void); +extern void tux_spinning_update_left_motor(void); +extern void tux_spinning_update_right_motor(void); +extern void tux_spinning_update_movements_remaining(void); + +extern bool tux_spinning_cmd_speed(unsigned char speed); +extern bool tux_spinning_cmd_left_on(unsigned char counter); +extern bool tux_spinning_cmd_right_on(unsigned char counter); +extern bool tux_spinning_cmd_left_on_during(float timeout); +extern bool tux_spinning_cmd_right_on_during(float timeout); +extern bool tux_spinning_cmd_off(void); + +#endif /* _TUX_SPINNING_H_ */ diff --git a/src/tux_sw_status.c b/src/tux_sw_status.c new file mode 100644 index 0000000..ea9535d --- /dev/null +++ b/src/tux_sw_status.c @@ -0,0 +1,672 @@ +/* + * Tux Droid - High level status + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +/* +This file contains the eventing code for the tux +values stored are the last evented values (so not the current values) +there is no external function that allows retrieving that value so it +was decided not to store the current value as there was no need for it +*/ + +#include +#include +#include +#include + +#ifdef USE_MUTEX +# include "threading_uniform.h" +#endif + +#include "tux_firmware.h" +#include "tux_hw_status.h" +#include "tux_misc.h" +#include "tux_sw_status.h" +#include "version.h" + +/* + notes on the following struct: + - strvalue is a pointer. it should only be used for static data + - intvalue is used for bool, uint8, int8 + - event_threshold is the threshold value; if the threshold is exceeded + the value is evented; for string values 0 means not evented, + 1 means evented if string changes +*/ +typedef struct +{ + int id; + char *name; + int value_fmt; + union { + int intvalue; + float floatvalue; + const char *strvalue; + }; + int event_threshold; + const char *value_doc; + double lu_time; +} sw_status_t; + +#ifdef USE_MUTEX +static mutex_t __status_mutex; +#endif +static event_callback_t event_funct; + +#define INIT_FLOATID(id, value_fmt, name, value_doc, initval, threshold) \ + { id, name, value_fmt, {.floatvalue = initval}, threshold, value_doc, 0.0 }, +#define INIT_INTID(id, value_fmt, name, value_doc, initval, threshold) \ + { id, name, value_fmt, {.intvalue = initval}, threshold, value_doc, 0.0 }, +#define INIT_STRINGID(id, value_fmt, name, value_doc, initval, threshold) \ + { id, name, value_fmt, {.strvalue = initval}, threshold, value_doc, 0.0 }, + +sw_status_t sw_status_table[SW_STATUS_NUMBER] = { + INIT_STRINGID(SW_ID_FLIPPERS_POSITION, ID_FMT_STRING, + "flippers_position", "DOWN|UP", STRING_VALUE_DOWN, 1) + + INIT_INTID(SW_ID_FLIPPERS_REMAINING_MVM, ID_FMT_UINT8, + "flippers_remaining_movements", "range[0..255]", 0, 1) + + INIT_STRINGID(SW_ID_SPINNING_DIRECTION, ID_FMT_STRING, + "spinning_direction", "NONE|LEFT|RIGHT", STRING_VALUE_NONE, 1) + + INIT_INTID(SW_ID_SPINNING_REMAINING_MVM, ID_FMT_UINT8, + "spinning_remaining_movements", "range[0..255]", 0, 1) + + INIT_INTID(SW_ID_LEFT_WING_BUTTON, ID_FMT_BOOL, + "left_wing_button", "False|True", 0, 1) + + INIT_INTID(SW_ID_RIGHT_WING_BUTTON, ID_FMT_BOOL, + "right_wing_button", "False|True", 0, 1) + + INIT_INTID(SW_ID_HEAD_BUTTON, ID_FMT_BOOL, + "head_button", "False|True", 0, 1) + + INIT_STRINGID(SW_ID_REMOTE_BUTTON, ID_FMT_STRING, + "remote_button", "K_|RELEASE", STRING_VALUE_RELEASE, 1) + + INIT_STRINGID(SW_ID_MOUTH_POSITION, ID_FMT_STRING, + "mouth_position", "OPEN|CLOSE|NDEF", STRING_VALUE_CLOSE, 1) + + INIT_INTID(SW_ID_MOUTH_REMAINING_MVM, ID_FMT_UINT8, + "mouth_remaining_movements", "range[0..255]", 0, 1) + + INIT_STRINGID(SW_ID_EYES_POSITION, ID_FMT_STRING, + "eyes_position", "OPEN|CLOSE|NDEF", STRING_VALUE_CLOSE, 1) + + INIT_INTID(SW_ID_EYES_REMAINING_MVM, ID_FMT_UINT8, + "eyes_remaining_movements", "range[0..255]", 0, 1) + + INIT_INTID(SW_ID_DESCRIPTOR_COMPLETE, ID_FMT_BOOL, + "descriptor_complete", "True", 0, 1) + + INIT_INTID(SW_ID_RF_STATE, ID_FMT_BOOL, + "radio_state", "False|True", 0, 1) + + INIT_INTID(SW_ID_DONGLE_PLUG, ID_FMT_BOOL, + "dongle_plug", "False|True", 0, 1) + + INIT_STRINGID(SW_ID_CHARGER_STATE, ID_FMT_STRING, + "charger_state", + "UNPLUGGED|CHARGING|PLUGGED_NO_POWER|TRICKLE|INHIBITED", + STRING_VALUE_UNPLUGGED, 1) + + INIT_INTID(SW_ID_BATTERY_LEVEL, ID_FMT_INT, + "battery_level", "range[4000..6500] (mV)", 0, 1) + + INIT_STRINGID(SW_ID_BATTERY_STATE, ID_FMT_STRING, + "battery_state", "EMPTY|LOW|HIGH|FULL", STRING_VALUE_EMPTY, 1) + + INIT_INTID(SW_ID_LIGHT_LEVEL, ID_FMT_FLOAT, + "light_level", "range[0.0..100.0]", 0.0, 1000) + + INIT_STRINGID(SW_ID_LEFT_LED_STATE, ID_FMT_STRING, + "left_led_state", "ON|OFF|CHANGING", STRING_VALUE_OFF, 1) + + INIT_STRINGID(SW_ID_RIGHT_LED_STATE, ID_FMT_STRING, + "right_led_state", "ON|OFF|CHANGING", STRING_VALUE_OFF, 1) + + INIT_INTID(SW_ID_CONNECTION_QUALITY, ID_FMT_INT, + "connection_quality", "range[0..100]", 0, 1) + + INIT_STRINGID(SW_ID_AUDIO_FLASH_PLAY, ID_FMT_STRING, + "audio_flash_play", "TRACK_|STOP", STRING_VALUE_STOP, 1) + + INIT_INTID(SW_ID_AUDIO_GENERAL_PLAY, ID_FMT_BOOL, + "audio_general_play", "False|True", 0, 1) + + INIT_INTID(SW_ID_FLASH_PROG_CURR_TRACK, ID_FMT_UINT8, + "flash_programming_current_track", "range[0..255]", 0, 1) + + INIT_INTID(SW_ID_FLASH_PROG_LAST_TRACK_SIZE, ID_FMT_INT, + "flash_programming_last_track_size", "", 0, 1) + + INIT_STRINGID(SW_ID_TUXCORE_SYMBOLIC_VERSION, ID_FMT_STRING, + "tuxcore_symbolic_version", "", + knowed_tuxcore_symbolic_version, 1) + + INIT_STRINGID(SW_ID_TUXAUDIO_SYMBOLIC_VERSION, ID_FMT_STRING, + "tuxaudio_symbolic_version", "", + knowed_tuxaudio_symbolic_version, 1) + + INIT_STRINGID(SW_ID_FUXUSB_SYMBOLIC_VERSION, ID_FMT_STRING, + "fuxusb_symbolic_version", "", + knowed_fuxusb_symbolic_version, 1) + + INIT_STRINGID(SW_ID_FUXRF_SYMBOLIC_VERSION, ID_FMT_STRING, + "fuxrf_symbolic_version", "", + knowed_fuxrf_symbolic_version, 1) + + INIT_STRINGID(SW_ID_TUXRF_SYMBOLIC_VERSION, ID_FMT_STRING, + "tuxrf_symbolic_version", "", + knowed_tuxrf_symbolic_version, 1) + + INIT_STRINGID(SW_ID_DRIVER_SYMBOLIC_VERSION, ID_FMT_STRING, + "driver_symbolic_version", "", + "", 1) + + INIT_FLOATID(SW_ID_SOUND_REFLASH_BEGIN, ID_FMT_FLOAT, + "sound_reflash_begin", "", 0.0, 1000) + + INIT_STRINGID(SW_ID_SOUND_REFLASH_END, ID_FMT_STRING, + "sound_reflash_end", "NO_ERROR|ERROR_RF_OFFLINE|ERROR_WAV|ERROR_USB", + STRING_VALUE_NO_ERROR, 1) + + INIT_INTID(SW_ID_SOUND_REFLASH_CURRENT_TRACK, ID_FMT_UINT8, + "sound_reflash_current_track", "range[0..255]", 0, 1) + + INIT_INTID(SW_ID_EYES_MOTOR_ON, ID_FMT_BOOL, + "eyes_motor_on", "False|True", 0, 1) + + INIT_INTID(SW_ID_MOUTH_MOTOR_ON, ID_FMT_BOOL, + "mouth_motor_on", "False|True", 0, 1) + + INIT_INTID(SW_ID_FLIPPERS_MOTOR_ON, ID_FMT_BOOL, + "flippers_motor_on", "False|True", 0, 1) + + INIT_INTID(SW_ID_SPIN_LEFT_MOTOR_ON, ID_FMT_BOOL, + "spin_left_motor_on", "False|True", 0, 1) + + INIT_INTID(SW_ID_SPIN_RIGHT_MOTOR_ON, ID_FMT_BOOL, + "spin_right_motor_on", "False|True", 0, 1) + + INIT_INTID(SW_ID_FLASH_SOUND_COUNT, ID_FMT_UINT8, + "sound_flash_count", "range[0..255]", 0, 1) +}; + +/** + * + */ +LIBLOCAL void +tux_sw_status_init(void) +{ + static char driver_symbolic_version[128] = ""; + int i; + +#ifdef USE_MUTEX + mutex_init(__status_mutex); +#endif + + sprintf(driver_symbolic_version, "libtuxdriver_%d.%d.%d-r%d", + VER_MAJOR, + VER_MINOR, + VER_UPDATE, + VER_REVISION); + + tux_sw_status_set_strvalue(SW_ID_DRIVER_SYMBOLIC_VERSION, + driver_symbolic_version, false); + + /* Initialize the "last updated time" value of the statuses */ + for (i = 0; i < SW_STATUS_NUMBER; i++) + { + sw_status_table[i].lu_time = get_time(); + } + + +#ifdef GENERATE_DOC + tux_sw_status_dump_status_doc(); +#endif +} + +/** + * + */ +LIBLOCAL TuxDrvError +tux_sw_status_name_from_id(int id, char *name) +{ + if ((id < 0) || (id >= SW_STATUS_NUMBER)) + { + strcpy(name, "UNKNOW"); + return E_TUXDRV_INVALIDIDENTIFIER; + } + + strcpy(name, sw_status_table[id].name); + return E_TUXDRV_NOERROR; +} + +/** + * + */ +LIBLOCAL TuxDrvError +tux_sw_status_id_from_name(const char *name, int *id) +{ + int i = -1; + +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + for (i = 0; i < SW_STATUS_NUMBER; i++) + { + if (!(strcmp(name, sw_status_table[i].name))) + { + *id = i; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + return E_TUXDRV_NOERROR; + } + } +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + + return E_TUXDRV_INVALIDNAME; +} + +/** + * + */ +LIBLOCAL const char * +tux_sw_status_value_fmt_from_id(int id) +{ + switch (id) { + case ID_FMT_BOOL: + return "bool"; + case ID_FMT_UINT8: + return "uint8"; + case ID_FMT_INT: + return "int"; + case ID_FMT_FLOAT: + return "float"; + case ID_FMT_STRING: + return "string"; + default: + return "UNKNOW"; + } +} + +#include "log.h" +/** + * + */ +static void +get_status_value_str(int id, char *str) +{ +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + switch (sw_status_table[id].value_fmt) { + case ID_FMT_BOOL: + if (sw_status_table[id].intvalue) + { + strcpy(str, "True"); + } + else + { + strcpy(str, "False"); + } + break; + case ID_FMT_UINT8: + sprintf(str, "%d", sw_status_table[id].intvalue); + break; + case ID_FMT_INT: + sprintf(str, "%d", sw_status_table[id].intvalue); + break; + case ID_FMT_FLOAT: + sprintf(str, "%f", sw_status_table[id].floatvalue); + break; + case ID_FMT_STRING: + strcpy(str, sw_status_table[id].strvalue); + break; + default: + break; + } +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + + return; +} + +/** + * + */ +LIBLOCAL TuxDrvError +tux_sw_status_get_state_str(int id, char *state) +{ + const char *fmt_str; + char name_str[128] = ""; + char value_str[128] = ""; + TuxDrvError err; + +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + fmt_str = tux_sw_status_value_fmt_from_id(sw_status_table[id].value_fmt); +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + err = tux_sw_status_name_from_id(id, name_str); + if (err != E_TUXDRV_NOERROR) + { + return err; + } + + get_status_value_str(id, value_str); + sprintf(state, "%s:%s:%s:%.3f", + sw_status_table[id].name, + fmt_str, + value_str, + (get_time() - sw_status_table[id].lu_time)); + + return E_TUXDRV_NOERROR; +} + +/** + * + */ +LIBLOCAL TuxDrvError +tux_sw_status_get_value_str(int id, char *value) +{ + if ((id < 0) || (id >= SW_STATUS_NUMBER)) + { + strcpy(value, "NULL"); + return E_TUXDRV_INVALIDIDENTIFIER; + } + + get_status_value_str(id, value); + + return E_TUXDRV_NOERROR; +} + +/** + * + */ +LIBLOCAL void +tux_sw_status_get_all_state_str(char *state) +{ + char *p; + + p = state; /* p always points to the end of the string */ + char tmp_state[256] = ""; + int i; + + for (i = 0; i < SW_STATUS_NUMBER; i++) + { + tux_sw_status_get_state_str(i, tmp_state); + p = p + sprintf(p, "%s\n", tmp_state); + } +} + +/** + * + */ +LIBLOCAL void +tux_sw_status_set_intvalue(int id, int value, bool make_event) +{ + int delta; + char state_str[1024]; + + if (make_event) + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + + delta = sw_status_table[id].intvalue - value; + if (delta < 0) + { + delta = -delta; + } + +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + + if (event_funct) + { + if (delta >= sw_status_table[id].event_threshold) + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + sw_status_table[id].intvalue = value; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + tux_sw_status_get_state_str(id, state_str); + event_funct(state_str); + } + } + } + else + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + sw_status_table[id].intvalue = value; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + } + +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + + sw_status_table[id].lu_time = get_time(); + +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_sw_status_set_floatvalue(int id, float value, bool make_event) +{ + float delta; + char state_str[1024]; + + if (make_event) + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + + delta = sw_status_table[id].floatvalue - value; + if (delta < 0) + { + delta = -delta; + } + +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + + if (event_funct) + { + if ((1000*delta) >= sw_status_table[id].event_threshold) + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + sw_status_table[id].floatvalue = value; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + tux_sw_status_get_state_str(id, state_str); + event_funct(state_str); + } + } + } + else + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + sw_status_table[id].floatvalue = value; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + } + +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + + sw_status_table[id].lu_time = get_time(); + +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_sw_status_set_strvalue(int id, const char *value, bool make_event) +{ + char state_str[1024]; + + if (make_event) + { + if (event_funct) + { + /* + the next if statement uses pointer comparison + this works like a charm under the following two conditions + - value points to a string constants (and not variables) + - no duplicate string constants (althought the compiler might + pick up this one anyway). + If the first condition is not met, we need to copy the string + instead of the pointer (and allocate space for it), + (or resolve it in the caller, not really a nice solution) + If the second condtion is not met something like: + ((sw_status_table[id].strvalue == NULL) || + strcmp(sw_status_table[id].strvalue,value))) + could be done instead of the value != line) + */ + if (sw_status_table[id].event_threshold && + (value != sw_status_table[id].strvalue)) + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + sw_status_table[id].strvalue = value; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + tux_sw_status_get_state_str(id, state_str); + event_funct(state_str); + } + } + } + else + { +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + sw_status_table[id].strvalue = value; +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif + } + +#ifdef USE_MUTEX + mutex_lock(__status_mutex); +#endif + + sw_status_table[id].lu_time = get_time(); + +#ifdef USE_MUTEX + mutex_unlock(__status_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_sw_status_set_event_callback(event_callback_t funct) +{ + event_funct = funct; +} + +/** + * + */ +LIBLOCAL void +tux_sw_status_dump_status_doc(void) +{ + int i; + char status_doc[8192] = ""; + char value_str[256] = ""; + FILE *doc_file; + char *p; + + p = status_doc; /* point to the end of the string */ + strcpy(p, "Tux status documentation :\n" + "--------------------------\n\n"); + + p = p + strlen(p); + + for (i = 0; i < SW_STATUS_NUMBER; i++) + { + tux_sw_status_get_state_str(i, value_str); + p = p + sprintf(p, + "Status %.2d:\n" + " ID : %d\n" + " Name : %s\n" + " Value type : %s\n" + " Possible values : %s\n" + " Default state : [%s]\n\n", + i, + i, + sw_status_table[i].name, + tux_sw_status_value_fmt_from_id(sw_status_table[i].value_fmt), + sw_status_table[i].value_doc, + value_str + ); + } + + doc_file = fopen(STATUS_DOC_FILE_PATH, "w"); + if (doc_file) + { + fprintf(doc_file, "%s\n", status_doc); + fclose(doc_file); + } + else + { + printf("file error %s\n", strerror(errno)); + } +} + diff --git a/src/tux_sw_status.h b/src/tux_sw_status.h new file mode 100644 index 0000000..1ac7ce5 --- /dev/null +++ b/src/tux_sw_status.h @@ -0,0 +1,127 @@ +/* + * Tux Droid - High level status + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_SW_STATUS_H_ +#define _TUX_SW_STATUS_H_ + +#include + +#include "tux_error.h" + +#define STRING_VALUE_ON "ON" +#define STRING_VALUE_OFF "OFF" +#define STRING_VALUE_CHANGING "CHANGING" +#define STRING_VALUE_FULL "FULL" +#define STRING_VALUE_HIGH "HIGH" +#define STRING_VALUE_LOW "LOW" +#define STRING_VALUE_EMPTY "EMPTY" +#define STRING_VALUE_OPEN "OPEN" +#define STRING_VALUE_CLOSE "CLOSE" +#define STRING_VALUE_NDEF "NDEF" +#define STRING_VALUE_NONE "NONE" +#define STRING_VALUE_LEFT "LEFT" +#define STRING_VALUE_RIGHT "RIGHT" +#define STRING_VALUE_DOWN "DOWN" +#define STRING_VALUE_UP "UP" +#define STRING_VALUE_UNPLUGGED "UNPLUGGED" +#define STRING_VALUE_CHARGING "CHARGING" +#define STRING_VALUE_PLUGGED_NO_POWER "PLUGGED_NO_POWER" +#define STRING_VALUE_TRICKLE "TRICKLE" +#define STRING_VALUE_INHIBITED "INHIBITED" +#define STRING_VALUE_STOP "STOP" +#define STRING_VALUE_TRACK "TRACK" +#define STRING_VALUE_RELEASE "RELEASE" +#define STRING_VALUE_NO_ERROR "NO_ERROR" +#define STRING_VALUE_ERROR_RF_OFFLINE "ERROR_RF_OFFLINE" +#define STRING_VALUE_ERROR_WAV_ERROR "ERROR_WAV" +#define STRING_VALUE_ERROR_USB "ERROR_USB" + +typedef enum { + SW_ID_FLIPPERS_POSITION = 0, + SW_ID_FLIPPERS_REMAINING_MVM, + SW_ID_SPINNING_DIRECTION, + SW_ID_SPINNING_REMAINING_MVM, + SW_ID_LEFT_WING_BUTTON, + SW_ID_RIGHT_WING_BUTTON, + SW_ID_HEAD_BUTTON, + SW_ID_REMOTE_BUTTON, + SW_ID_MOUTH_POSITION, + SW_ID_MOUTH_REMAINING_MVM, + SW_ID_EYES_POSITION, + SW_ID_EYES_REMAINING_MVM, + SW_ID_DESCRIPTOR_COMPLETE, + SW_ID_RF_STATE, + SW_ID_DONGLE_PLUG, + SW_ID_CHARGER_STATE, + SW_ID_BATTERY_LEVEL, + SW_ID_BATTERY_STATE, + SW_ID_LIGHT_LEVEL, + SW_ID_LEFT_LED_STATE, + SW_ID_RIGHT_LED_STATE, + SW_ID_CONNECTION_QUALITY, + SW_ID_AUDIO_FLASH_PLAY, + SW_ID_AUDIO_GENERAL_PLAY, + SW_ID_FLASH_PROG_CURR_TRACK, + SW_ID_FLASH_PROG_LAST_TRACK_SIZE, + SW_ID_TUXCORE_SYMBOLIC_VERSION, + SW_ID_TUXAUDIO_SYMBOLIC_VERSION, + SW_ID_FUXUSB_SYMBOLIC_VERSION, + SW_ID_FUXRF_SYMBOLIC_VERSION, + SW_ID_TUXRF_SYMBOLIC_VERSION, + SW_ID_DRIVER_SYMBOLIC_VERSION, + SW_ID_SOUND_REFLASH_BEGIN, + SW_ID_SOUND_REFLASH_END, + SW_ID_SOUND_REFLASH_CURRENT_TRACK, + SW_ID_EYES_MOTOR_ON, + SW_ID_MOUTH_MOTOR_ON, + SW_ID_FLIPPERS_MOTOR_ON, + SW_ID_SPIN_LEFT_MOTOR_ON, + SW_ID_SPIN_RIGHT_MOTOR_ON, + SW_ID_FLASH_SOUND_COUNT, + SW_STATUS_NUMBER // SW_STATUS_NUMBER must be last and may not be removed !! +} SW_ID; + + +typedef enum { + ID_FMT_BOOL = 0, + ID_FMT_UINT8, + ID_FMT_INT, + ID_FMT_FLOAT, + ID_FMT_STRING, +} ID_FMT; + +#define STATUS_DOC_FILE_PATH "./status_doc.txt" + +typedef void(*event_callback_t)(char *event); + +extern void tux_sw_status_init(void); +extern void tux_sw_status_set_intvalue(int id, int value, bool make_event); +extern void tux_sw_status_set_strvalue(int id, const char *value, bool make_event); +extern void tux_sw_status_set_floatvalue(int id, float value, bool make_event); +extern TuxDrvError tux_sw_status_name_from_id(int id, char* name); +extern TuxDrvError tux_sw_status_id_from_name(const char* name, int *id); +extern const char *tux_sw_status_value_fmt_from_id(int id); +extern TuxDrvError tux_sw_status_get_state_str(int id, char *state); +extern TuxDrvError tux_sw_status_get_value_str(int id, char *value); +extern void tux_sw_status_get_all_state_str(char *state); +extern void tux_sw_status_set_event_callback(event_callback_t funct); +extern void tux_sw_status_dump_status_doc(void); + +#endif /* _TUX_SW_STATUS_H_ */ diff --git a/src/tux_types.h b/src/tux_types.h new file mode 100644 index 0000000..17bbe4d --- /dev/null +++ b/src/tux_types.h @@ -0,0 +1,238 @@ +/* + * Tux Droid - tux types + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_TYPES_H_ +#define _TUX_TYPES_H_ + +#include "tux_leds.h" +#include "tux_movements.h" + +typedef unsigned char data_frame[4]; +typedef unsigned char raw_frame[5]; + +#define CMDSIZE 1024 +#define MACROSIZE 16384 + +/* command groups */ +typedef enum { + NO_CMD = 0, + TUX_CMD, + RAW_CMD +} tux_command_group_t; + +/* commands */ +typedef enum { + AUDIO, + EYES, + IR, + LED, + MOUTH, + SOUND_FLASH, + SPINNING, + FLIPPERS +} tux_command_t; + +/* subcommands */ +typedef enum { + BLINK, + CHANNEL_GENERAL, + CHANNEL_TTS, + CLOSE, + DOWN, + LEFT_ON, + LEFT_ON_DURING, + MUTE, + OFF, + ON, + ON_DURING, + OPEN, + PLAY, + PULSE, + RIGHT_ON, + RIGHT_ON_DURING, + SEND, + SET, + SPEED, + UP +} tux_sub_command_t; + +/* + The data structures for the invididual commands. + It was decided to make separate structures for each subcommand + (instead of e.g. clubbing all parameters together) + Rationale: + separate structures make it more straigthforward to add a new subcommand. + separate structures make it simpler to create a C api. + depending on the compiler this could also result in stricter type + checking + For now the types are not used to communicate between parser and + individual functions. + Note that storage waste by duplicate data fields is recouped + as all data is in a union +*/ + +/* audio */ +typedef struct { + bool muteflag; +} audio_mute_parameters_t; + +/* eyes */ +typedef struct { + move_final_state_t state; + unsigned char nr_movements; +} eyes_on_parameters_t; + +typedef struct { + move_final_state_t state; + float duration; +} eyes_on_during_parameters_t; + +/* ir */ +typedef struct { + unsigned char address; + unsigned char command; +} ir_send_parameters_t; + +/* leds */ +typedef struct { + leds_t leds; + float intensity; +} led_on_parameters_t; + +typedef struct { + leds_t leds; +} led_off_parameters_t; + +typedef struct { + leds_t leds; + float min_intensity; + float max_intensity; + unsigned char pulse_count; + float pulse_period; + effect_type_t effect_type; + float effect_speed; + unsigned char effect_step; +} led_pulse_parameters_t; + +typedef struct { + leds_t leds; + unsigned char pulse_count; + float pulse_period; +} led_blink_parameters_t; + +typedef struct { + leds_t leds; + float intensity; + effect_type_t effect_type; + float effect_speed; + unsigned char effect_step; +} led_set_parameters_t; + +/* mouth */ +typedef struct { + move_final_state_t state; + unsigned char nr_movements; +} mouth_on_parameters_t; + +typedef struct { + move_final_state_t state; + float duration; +} mouth_on_during_parameters_t; + +/* sound flash */ +typedef struct { + unsigned char track; + float volume; +} sound_flash_play_parameters_t; + +/* spinning */ +typedef struct { + unsigned char nr_qturns; +} spinning_on_parameters_t; + +typedef struct { + float duration; +} spinning_on_during_parameters_t; + +typedef struct { + unsigned char speed; +} spinning_speed_parameters_t; + +/* flippers */ +typedef struct { + move_final_state_t state; + unsigned char nr_movements; +} flippers_on_parameters_t; + +typedef struct { + move_final_state_t state; + float duration; +} flippers_on_during_parameters_t; + +typedef struct { + unsigned char speed; +} flippers_speed_parameters_t; + +/* wifi avoidance */ +typedef struct { + unsigned char channel; +} wifi_avoid_channel_parameters_t; + +/* raw */ +typedef struct { + unsigned char raw[5]; +} raw_parameters_t; + +/* + this is the struct which contains all commands and arguments + the union is there to save storage + based upon command/sub_command we exactly know which union field we need +*/ +typedef struct { + double timeout; + tux_command_group_t command_group; + tux_command_t command; + tux_sub_command_t sub_command; + union { + audio_mute_parameters_t audio_mute_parameters; + eyes_on_parameters_t eyes_on_parameters; + eyes_on_during_parameters_t eyes_on_during_parameters; + ir_send_parameters_t ir_send_parameters; + led_on_parameters_t led_on_parameters; + led_off_parameters_t led_off_parameters; + led_pulse_parameters_t led_pulse_parameters; + led_blink_parameters_t led_blink_parameters; + led_set_parameters_t led_set_parameters; + mouth_on_parameters_t mouth_on_parameters; + mouth_on_during_parameters_t mouth_on_during_parameters; + sound_flash_play_parameters_t sound_flash_play_parameters; + spinning_on_parameters_t spinning_on_parameters; + spinning_on_during_parameters_t spinning_on_during_parameters; + spinning_speed_parameters_t spinning_speed_parameters; + flippers_on_parameters_t flippers_on_parameters; + flippers_on_during_parameters_t flippers_on_during_parameters; + flippers_speed_parameters_t flippers_speed_parameters; + wifi_avoid_channel_parameters_t wifi_avoid_channel_parameters; + raw_parameters_t raw_parameters; + }; + float inserted_at_time; +} delay_cmd_t; + +#endif /* _TUX_TYPES_H_ */ diff --git a/src/tux_usb.c b/src/tux_usb.c new file mode 100644 index 0000000..edbf850 --- /dev/null +++ b/src/tux_usb.c @@ -0,0 +1,660 @@ +/* + * Tux Droid - USB interface + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include + +#include "log.h" +#ifdef WIN32 +# include "tux_hid_win32.h" +#else +# include "tux_hid_unix.h" +#endif +#include "tux_types.h" +#include "tux_usb.h" + +#ifdef USE_MUTEX +# include "threading_uniform.h" +#endif + +static bool usb_connected = false; +static frame_callback_t frame_callback_function; +static simple_callback_t dongle_disconnect_function; +static simple_callback_t dongle_connect_function; +static simple_callback_t loop_cycle_complete_function; +static rf_state_callback_t rf_state_callback_function; +static unsigned char last_knowed_rf_state = 0; +static char frame_status_request[5] = {1, 1, 0, 0, 0}; +static char frame_reset_dongle[5] = {1, 1, 0, 0, 0xFE}; +static char frame_reset_rf[5] = {1, 1, 0, 0, 0xFD}; +static char frame_blink_eyes[5] = {0, 0x40, 2, 0, 0}; + +#ifdef USE_MUTEX +static mutex_t __connected_mutex; +static mutex_t __read_write_mutex; +static mutex_t __callback_mutex; +#endif + +static bool read_loop_started = false; + +static void set_connected(bool value); + +static void set_read_loop_started(bool value); +static bool get_read_loop_started(void); +static void read_usb_loop(void); + +#ifdef USB_IDFRAME +static int id_frame_last = 999; +static int freezed_frame_cnt = 0; +#endif +static int empty_frame_cnt = 0; + +/** + * + */ +LIBLOCAL void +tux_usb_set_frame_callback(frame_callback_t funct) +{ +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + frame_callback_function = funct; +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_usb_set_rf_state_callback(rf_state_callback_t funct) +{ +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + rf_state_callback_function = funct; +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_usb_set_disconnect_dongle_callback(simple_callback_t funct) +{ +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + dongle_disconnect_function = funct; +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_usb_set_connect_dongle_callback(simple_callback_t funct) +{ +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + dongle_connect_function = funct; +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_usb_set_loop_cycle_complete_callback(simple_callback_t funct) +{ +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + loop_cycle_complete_function = funct; +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_usb_init_module(void) +{ +#ifdef USE_MUTEX + mutex_init(__connected_mutex); + mutex_init(__read_write_mutex); + mutex_init(__callback_mutex); +#endif +} + +/** + * + */ +LIBLOCAL void +tux_usb_exit_module(void) +{ +#ifdef USE_MUTEX + mutex_delete(__connected_mutex); + mutex_delete(__read_write_mutex); + mutex_delete(__callback_mutex); +#endif +} + +/** + * + */ +static void +set_connected(bool value) +{ +#ifdef USE_MUTEX + mutex_lock(__connected_mutex); +#endif + usb_connected = value; +#ifdef USE_MUTEX + mutex_unlock(__connected_mutex); +#endif + if (value) + { + if (dongle_connect_function) + { + dongle_connect_function(); + } + } + else + { + if (dongle_disconnect_function) + { + dongle_disconnect_function(); + } + } +} + +/** + * + */ +LIBLOCAL bool +tux_usb_connected(void) +{ + bool ret = false; + +#ifdef USE_MUTEX + mutex_lock(__connected_mutex); +#endif + ret = usb_connected; +#ifdef USE_MUTEX + mutex_unlock(__connected_mutex); +#endif + + return ret; +} + +/** + * + */ +LIBLOCAL TuxUSBError +tux_usb_capture(void) +{ + empty_frame_cnt = 0; +#ifdef USB_IDFRAME + id_frame_last = 999; + freezed_frame_cnt = 0; +#endif + + if (!tux_hid_capture(TUX_VID, TUX_PID)) + { + return TuxUSBFuxNotFound; + } + + set_connected(true); + + return TuxUSBNoError; +} + +/** + * + */ +LIBLOCAL TuxUSBError +tux_usb_release(void) +{ + tux_hid_release(); + + set_connected(false); + + return TuxUSBNoError; +} + +/** + * + */ +LIBLOCAL TuxUSBError +tux_usb_write(const void *buff) +{ + bool ret; + + if (!tux_usb_connected()) + { + log_error("Fux USB device not connected"); + return TuxUSBNotConnected; + } + +#ifdef USE_MUTEX + mutex_lock(__read_write_mutex); +#endif + + ret = tux_hid_write(TUX_SEND_LENGTH, (char *)buff); +#ifdef USE_MUTEX + mutex_unlock(__read_write_mutex); +#endif + + if (!ret) + { + set_connected(false); + tux_usb_release(); + log_error("Fux is disconnected"); + return TuxUSBDisconnected; + } + return TuxUSBNoError; +} + +/** + * + */ +static void +process_usb_frame(const char *data) +{ + int i, j; + int rf_state; + int packet_count; + int id_frame; + char *data_buf; + char packet_data[4]; + + id_frame = data[0]; + rf_state = data[1]; + packet_count = data[3]; + data_buf = (char *)data; + data_buf += 4; + +#ifdef USB_IDFRAME + /* Check if the frame is newer than the last received one */ + if (id_frame == id_frame_last) + { + freezed_frame_cnt++; + log_warning("The id of USB frame is the same than the previous [%d]", + freezed_frame_cnt); +#ifndef USB_DEBUG + if (freezed_frame_cnt >= TUX_USB_FREEZED_FRAMES_LIMIT) + { + freezed_frame_cnt = 0; + id_frame_last = 999; + log_error("The USB frame retrieving seems to be freezed [%d]", + TUX_USB_FREEZED_FRAMES_LIMIT); + log_info("The RF connection will be reinitialized"); + tux_usb_rf_reset(); + } +#endif + return; + } + else + { + freezed_frame_cnt = 0; + id_frame_last = id_frame; + } +#endif + + /* Having RF state to ON and no status frame is not normal */ + if ((packet_count == 0) && (rf_state == 1)) + { + empty_frame_cnt++; +#ifndef USB_DEBUG + if (empty_frame_cnt > 2) + { + log_warning("Consecutive frames without status : %d", empty_frame_cnt); + } + if (empty_frame_cnt >= TUX_USB_ERROR_LIMIT) + { + log_error("DONGLE ERROR : Too many consecutive frames without status [%d], but the RF is online", + TUX_USB_ERROR_LIMIT); + empty_frame_cnt = 0; + log_info("Send a command to the eyes."); + tux_usb_send_raw((unsigned char *)frame_blink_eyes); + } +#else + log_warning("Consecutive frames without status : %d", empty_frame_cnt); +#endif + } + else + { + empty_frame_cnt = 0; + } + + if (last_knowed_rf_state != rf_state) + { + last_knowed_rf_state = rf_state; +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + if (rf_state_callback_function) + { + rf_state_callback_function(last_knowed_rf_state); + } +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif + } + + if (packet_count > 15) + { + log_error("DONGLE ERROR : Statuses packets count is wrong (>15)"); + return; + } + + for (i = 0; i < packet_count; i++) + { + for (j = 0; j < 4; j++) + { + packet_data[j] = (unsigned char)data_buf[j]; + } +#ifdef USE_MUTEX + mutex_lock(__callback_mutex); +#endif + if (frame_callback_function) + { + frame_callback_function((unsigned char*)packet_data); + } +#ifdef USE_MUTEX + mutex_unlock(__callback_mutex); +#endif + + data_buf += 4; + } +} + +/** + * + */ +LIBLOCAL TuxUSBError +tux_usb_read(void *buf) +{ + bool ret; + + if (!tux_usb_connected()) + { + log_warning("Fux USB device not connected"); + return TuxUSBNotConnected; + } + + memset(buf, 0, TUX_RECEIVE_LENGTH); + +#ifdef USE_MUTEX + mutex_lock(__read_write_mutex); +#endif + ret = tux_hid_write(TUX_SEND_LENGTH, (char *)frame_status_request); + if (!ret) + { +#ifdef USE_MUTEX + mutex_unlock(__read_write_mutex); +#endif + set_connected(false); + tux_usb_release(); + log_error("Fux is disconnected"); + return TuxUSBDisconnected; + } + +#ifndef WIN32 + /* Hid read write are not bocking on linux */ + usleep(10000); +#endif + + ret = tux_hid_read(TUX_RECEIVE_LENGTH, (char *)buf); +#ifdef USE_MUTEX + mutex_unlock(__read_write_mutex); +#endif + + if (!ret) + { + set_connected(false); + tux_usb_reset(); + tux_usb_release(); + log_error("Fux is disconnected"); + return TuxUSBDisconnected; + } + + process_usb_frame((char *)buf); + + return TuxUSBNoError; +} + +/** + * + */ +static void +set_read_loop_started(bool value) +{ +#ifdef USE_MUTEX + mutex_lock(__connected_mutex); +#endif + read_loop_started = value; +#ifdef USE_MUTEX + mutex_unlock(__connected_mutex); +#endif +} + +/** + * + */ +static bool +get_read_loop_started(void) +{ + bool ret = false; +#ifdef USE_MUTEX + mutex_lock(__connected_mutex); +#endif + ret = read_loop_started; +#ifdef USE_MUTEX + mutex_unlock(__connected_mutex); +#endif + + return ret; +} + +/** + * + */ +static void +read_usb_loop(void) +{ + double initial_timeout = 0.0; + double current_timeout = 0.0; + unsigned char data[64] = { [0 ... 63] = 0 }; + + initial_timeout = get_time(); + current_timeout = initial_timeout; + + set_read_loop_started(true); + + log_info("Start the read loop"); + + while (tux_usb_connected()) + { + current_timeout += TUX_READ_LOOP_INTERVAL; + + if (!tux_usb_connected()) + { + break; + } + + tux_usb_read(data); + + if (loop_cycle_complete_function) + { + loop_cycle_complete_function(); + } + + while (get_time() < current_timeout) + { + usleep(1000); + } + + current_timeout = get_time(); + } + + set_read_loop_started(false); + + log_info("Read loop stopped"); +} + +/** + * + */ +LIBLOCAL TuxUSBError +tux_usb_start(void) +{ + int ret; + + if (get_read_loop_started()) + { + return TuxUSBAlreadyStarted; + } + + ret = tux_usb_capture(); + if (ret != TuxUSBNoError) + { + return ret; + } + + last_knowed_rf_state = 0; + + read_usb_loop(); + usleep(100000); + + return TuxUSBNoError; +} + +/** + * + */ +LIBLOCAL TuxUSBError +tux_usb_stop(void) +{ + int ret; + + if (!tux_usb_connected()) + { + return TuxUSBNoError; + } + + set_connected(false); + + usleep(500000); + + ret = tux_usb_release(); + if (ret != TuxUSBNoError) + { + return ret; + } + + return TuxUSBNoError; +} + +/** + * + */ +LIBLOCAL void +tux_usb_reset(void) +{ + tux_hid_write(TUX_SEND_LENGTH, (char *)frame_reset_dongle); +} + +/** + * + */ +LIBLOCAL void +tux_usb_rf_reset(void) +{ + tux_hid_write(TUX_SEND_LENGTH, (char *)frame_reset_rf); +} + +/** + * Send a raw command + * @param data 5 bytes array + */ +LIBLOCAL bool +tux_usb_send_raw(const unsigned char* data) +{ + int ret; + + ret = tux_usb_write(data); + + usleep(10000); + + if (ret != TuxUSBNoError) + { + return false; + } + else + { + return true; + } +} + +/** + * Send a command to Tuxdroid. + * @param data 4 bytes array + */ +LIBLOCAL bool +tux_usb_send_to_tux(const unsigned char* data) +{ + raw_frame frame = {0, data[0], data[1], data[2], data[3]}; + + return tux_usb_send_raw(frame); +} + +/** + * Send a command to fux dongle. + * @param data 4 bytes array + */ +LIBLOCAL bool +tux_usb_send_to_dongle(const unsigned char* data) +{ + raw_frame frame = {1, data[0], data[1], data[2], data[3]}; + + return tux_usb_send_raw(frame); +} + +/** + * Get the rf state + * @param data 4 bytes array + */ +LIBLOCAL bool +tux_usb_get_rf_state(void) +{ + return last_knowed_rf_state; +} diff --git a/src/tux_usb.h b/src/tux_usb.h new file mode 100644 index 0000000..83b496b --- /dev/null +++ b/src/tux_usb.h @@ -0,0 +1,202 @@ +/* + * Tux Droid - USB interface + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_USB_H_ +#define _TUX_USB_H_ + +#include + +#ifndef WIN32 +#include +#endif + +#include "tux_misc.h" + +#define TUX_PID 0xFF07 +#define TUX_VID 0x03EB +#define TUX_SEND_LENGTH 5 +#define TUX_RECEIVE_LENGTH 64 +#define TUX_READ_LOOP_INTERVAL 0.1 +#define TUX_USB_ERROR_LIMIT 20 +#define TUX_USB_FREEZED_FRAMES_LIMIT 10 + +#ifdef WIN32 +# define usb_busses usb_get_busses() +#endif + +typedef int TuxUSBError; +typedef enum +{ + TuxUSBNoError = 0, + TuxUSBFuxNotFound, + TuxUSBCantClaimInterface, + TuxUSBHandleNotOpen, + TuxUSBCantReleaseInterface, + TuxUSBCantCloseDevice, + TuxUSBNotConnected, + TuxUSBWriteError, + TuxUSBWriteErrorRFDisconnected, + TuxUSBReadError, + TuxUSBDisconnected, + TuxUSBAlreadyStarted, + TuxUSBFirmwareTooOld, +} tux_usb_error_code_t; + +/** + * Callback function for the frame receiving event + * @param data A pointer to the frame + */ +typedef void(*frame_callback_t)(const unsigned char *data); + +/** + * Callback function prototype for the RF state event + * @param state state of rf connection + */ +typedef void(*rf_state_callback_t)(unsigned char state); + +/** Initialization of the module + Some mutex are initialized in this function. +*/ +extern void tux_usb_init_module(void); + +/** + * Finalization of the module + * Some mutex are finalizaed in this function. + */ +extern void tux_usb_exit_module(void); + +/** + * Capture of the usb handle + * @return An error code indicating the success of the operation. + * (TuxUSBNoError | TuxUSBFuxNotFound | TuxUSBFirmwareTooOld | + * TuxUSBHandleNotOpen) + */ +extern TuxUSBError tux_usb_capture(void); + +/** Release of the usb handle + @return An error code indicating the success of the operation. + (TuxUSBNoError | TuxUSBCantReleaseInterface | TuxUSBCantCloseDevice) +*/ +extern TuxUSBError tux_usb_release(void); + +/** Set the callback function for the frame event. + @param funct The function will be linked +*/ +extern void tux_usb_set_frame_callback(frame_callback_t funct); + +/** + * Set the callback function for the RF state event. + * @param funct The function will be linked + */ +extern void tux_usb_set_rf_state_callback(rf_state_callback_t funct); + +/** + * Set the callback function for the dongle disconnect event. + * @param funct The function will be linked + */ +extern void tux_usb_set_disconnect_dongle_callback(simple_callback_t funct); + +/** + * Set the callback function for the dongle connect event. + * @param funct The function will be linked + */ +extern void tux_usb_set_connect_dongle_callback(simple_callback_t funct); + +/** + * Set the callback function for a cycle has complete event. + * @param funct The function will be linked + */ +extern void tux_usb_set_loop_cycle_complete_callback(simple_callback_t funct); + +/** + * Write data on usb dongle + * @param buff Data to write + * @return An error code indicating the success of the operation + * (TuxUSBNoError | TuxUSBNotConnected | TuxUSBWriteError) + */ +extern TuxUSBError tux_usb_write(const void *buff); + +/** Read data from usb dongle + * @param buf Data pointer + * @return An error code indicating the success of the operation + * (TuxUSBNoError | TuxUSBNotConnected | TuxUSBWriteError | TuxUSBReadError + * | TuxUSBWriteErrorRFDisconnected) + */ +extern TuxUSBError tux_usb_read(void *buf); + +/** + * Start a loop that read the data on the usb dongle. + * The loop run in a thread. The duration of a cycle is 100msec. + * A frame event occuring at the end of each cycle. + * The function detect the dongle disconnection. + * @return An error code indicating the success of the operation + * (TuxUSBNoError | TuxUSBAlreadyStarted | TuxUSBFuxNotFound | + * TuxUSBCantClaimInterface | TuxUSBHandleNotOpen) + */ +extern TuxUSBError tux_usb_start(void); + +/** + * Stop the loop that read the data on the usb dongle. + * @return An error code indicating the success of the operation + * (TuxUSBNoError | TuxUSBCantReleaseInterface | TuxUSBCantCloseDevice) + */ +extern TuxUSBError tux_usb_stop(void); + +/** + * Get the usb connected state. + * @return An integer which indicate if the usb interface is captured. + */ +extern bool tux_usb_connected(void); + +/** + * Reset the usb dongle + */ +extern void tux_usb_reset(void); + +/** + * Reset the RF + */ +extern void tux_usb_rf_reset(void); + +/** + * Send a command to Tuxdroid. + * @param data 4 bytes array + */ +extern bool tux_usb_send_to_tux(const unsigned char* data); + +/** + * Send a command to fux dongle. + * @param data 4 bytes array + */ +extern bool tux_usb_send_to_dongle(const unsigned char* data); + +/** + * Send a raw command to fux dongle. + * @param data 4 bytes array + */ +extern bool tux_usb_send_raw(const unsigned char *data); + +/** + * Get the rf state + * @param data 4 bytes array + */ +extern bool tux_usb_get_rf_state(void); + +#endif /* _TUX_USB_H_ */ diff --git a/src/tux_user_inputs.c b/src/tux_user_inputs.c new file mode 100644 index 0000000..f4d08c0 --- /dev/null +++ b/src/tux_user_inputs.c @@ -0,0 +1,341 @@ +/* + * Tux Droid - User inputs + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include + +#include "tux_hw_cmd.h" +#include "tux_hw_status.h" +#include "tux_sw_status.h" +#include "tux_types.h" +#include "tux_usb.h" +#include "tux_user_inputs.h" + +static bool ir_received = false; +static bool ir_has_not_yet_been_used = true; +static unsigned char rc5_timeout_counter = 0; +static bool rc5_on_receiving = false; +static unsigned char rc5_last_toggle = 0; + +/** + * + */ +LIBLOCAL void +tux_user_inputs_init(void) +{ + ir_received = false; + rc5_on_receiving = false; + rc5_timeout_counter = 0; + rc5_last_toggle = 0; + ir_has_not_yet_been_used = true; +} + +/** + * + */ +LIBLOCAL void +tux_user_inputs_update_left_wing_button(void) +{ + unsigned char new_state; + + new_state = hw_status_table.sensors1.sensors.bits.left_wing_push_button; + + tux_sw_status_set_intvalue(SW_ID_LEFT_WING_BUTTON, new_state, true); +} + +/** + * + */ +LIBLOCAL void +tux_user_inputs_update_right_wing_button(void) +{ + unsigned char new_state; + + new_state = hw_status_table.sensors1.sensors.bits.right_wing_push_button; + + tux_sw_status_set_intvalue(SW_ID_RIGHT_WING_BUTTON, new_state, true); +} + +/** + * + */ +LIBLOCAL void +tux_user_inputs_update_head_button(void) +{ + unsigned char new_state; + + new_state = hw_status_table.sensors1.sensors.bits.head_push_button; + + tux_sw_status_set_intvalue(SW_ID_HEAD_BUTTON, new_state, true); +} + +/** + * + */ +static const char * +RC5_code_to_str(int code) +{ + switch (code) { + case K_DUMMY_RELEASE: + return "RELEASE"; + case K_0: + return "K_0"; + case K_1: + return "K_1"; + case K_2: + return "K_2"; + case K_3: + return "K_3"; + case K_4: + return "K_4"; + case K_5: + return "K_5"; + case K_6: + return "K_6"; + case K_7: + return "K_7"; + case K_8: + return "K_8"; + case K_9: + return "K_9"; + case K_STANDBY: + return "K_STANDBY"; + case K_MUTE: + return "K_MUTE"; + case K_VOLUMEPLUS: + return "K_VOLUMEPLUS"; + case K_VOLUMEMINUS: + return "K_VOLUMEMINUS"; + case K_ESCAPE: + return "K_ESCAPE"; + case K_YES: + return "K_YES"; + case K_NO: + return "K_NO"; + case K_BACKSPACE: + return "K_BACKSPACE"; + case K_STARTVOIP: + return "K_STARTVOIP"; + case K_RECEIVECALL: + return "K_RECEIVECALL"; + case K_HANGUP: + return "K_HANGUP"; + case K_STAR: + return "K_STAR"; + case K_SHARP: + return "K_SHARP"; + case K_RED: + return "K_RED"; + case K_GREEN: + return "K_GREEN"; + case K_BLUE: + return "K_BLUE"; + case K_YELLOW: + return "K_YELLOW"; + case K_CHANNELPLUS: + return "K_CHANNELPLUS"; + case K_CHANNELMINUS: + return "K_CHANNELMINUS"; + case K_UP: + return "K_UP"; + case K_DOWN: + return "K_DOWN"; + case K_LEFT: + return "K_LEFT"; + case K_RIGHT: + return "K_RIGHT"; + case K_OK: + return "K_OK"; + case K_FASTREWIND: + return "K_FASTREWIND"; + case K_FASTFORWARD: + return "K_FASTFORWARD"; + case K_PLAYPAUSE: + return "K_PLAYPAUSE"; + case K_STOP: + return "K_STOP"; + case K_RECORDING: + return "K_RECORDING"; + case K_PREVIOUS: + return "K_PREVIOUS"; + case K_NEXT: + return "K_NEXT"; + case K_MENU: + return "K_MENU"; + case K_MOUSE: + return "K_MOUSE"; + case K_ALT: + return "K_ALT"; + default: + return "NULL"; + } +} + +/** + * + */ +LIBLOCAL void +tux_user_inputs_update_RC5(void) +{ + const char *code_str = ""; + + if (rc5_on_receiving) + { + if (!ir_received) + { + rc5_timeout_counter++; + if (rc5_timeout_counter >= RC5_TIMEOUT) + { + /* remote button is released */ + code_str = RC5_code_to_str(K_DUMMY_RELEASE); + tux_sw_status_set_strvalue(SW_ID_REMOTE_BUTTON, code_str, true); + + rc5_timeout_counter = 0; + rc5_on_receiving = false; + } + } + else + { + rc5_timeout_counter = 0; + } + + if (rc5_last_toggle != hw_status_table.ir.rc5_code.bits.toggle) + { + /* remote button is released */ + code_str = RC5_code_to_str(K_DUMMY_RELEASE); + tux_sw_status_set_strvalue(SW_ID_REMOTE_BUTTON, code_str, true); + + /* remote button is pressed */ + if (hw_status_table.ir.rc5_code.bits.command <= LAST_VALID_K) + { + code_str = RC5_code_to_str(hw_status_table.ir.rc5_code.bits.command); + tux_sw_status_set_strvalue(SW_ID_REMOTE_BUTTON, code_str, true); + } + else + { + rc5_on_receiving = 0; + } + } + } + else + { /* not in receiving */ + if ((ir_received) && + ((rc5_last_toggle != hw_status_table.ir.rc5_code.bits.toggle) || + ir_has_not_yet_been_used)) + { + ir_has_not_yet_been_used = false; + rc5_timeout_counter = 0; + rc5_on_receiving = true; + + /* remote button is pressed */ + if (hw_status_table.ir.rc5_code.bits.command <= LAST_VALID_K) + { + code_str = RC5_code_to_str(hw_status_table.ir.rc5_code.bits.command); + tux_sw_status_set_strvalue(SW_ID_REMOTE_BUTTON, code_str, true); + } + else + { + rc5_on_receiving = false; + } + } + } + + rc5_last_toggle = hw_status_table.ir.rc5_code.bits.toggle; + + ir_received = false; +} + +/** + * + */ +LIBLOCAL void +tux_user_inputs_init_time_RC5(void) +{ + ir_received = true; +} + +/** + * + */ +LIBLOCAL void +tux_user_inputs_update_charger_state(void) +{ + char *new_state = ""; + + if (!hw_status_table.sensors1.sensors.bits.power_plug_insertion_switch) + { + new_state = STRING_VALUE_UNPLUGGED; + } + else + { + if (hw_status_table.sensors1.sensors.bits.charger_led_status) + { + new_state = STRING_VALUE_CHARGING; + } + else + { + if (hw_status_table.ports.portb.bits.charger_inhibit_signal) + { + new_state = STRING_VALUE_INHIBITED; + } + else + { + new_state = STRING_VALUE_TRICKLE; + } + } + } + + tux_sw_status_set_strvalue(SW_ID_CHARGER_STATE, new_state, true); +} + +/** + * + */ +LIBLOCAL bool +tux_user_inputs_cmd_ir_on(void) +{ + data_frame frame = {TURN_IR_ON_CMD, 0, 0, 0}; + + return tux_usb_send_to_tux(frame); +} + +/** + * + */ +LIBLOCAL bool +tux_user_inputs_cmd_ir_off(void) +{ + data_frame frame = {TURN_IR_OFF_CMD, 0, 0, 0}; + + return tux_usb_send_to_tux(frame); +} + +/** + * + */ +LIBLOCAL bool +tux_user_inputs_cmd_ir_send(unsigned char address, unsigned char command) +{ + data_frame frame = {IR_SEND_RC5_CMD, 0, 0, 0}; + + frame[1] = address; + frame[2] = command; + + return tux_usb_send_to_tux(frame); +} diff --git a/src/tux_user_inputs.h b/src/tux_user_inputs.h new file mode 100644 index 0000000..743b1d9 --- /dev/null +++ b/src/tux_user_inputs.h @@ -0,0 +1,87 @@ +/* + * Tux Droid - User inputs + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#ifndef _TUX_USER_INPUTS_H_ +#define _TUX_USER_INPUTS_H_ + +#include + +#define RC5_TIMEOUT 2 + +#define K_0 0x00 +#define K_1 0x01 +#define K_2 0x02 +#define K_3 0x03 +#define K_4 0x04 +#define K_5 0x05 +#define K_6 0x06 +#define K_7 0x07 +#define K_8 0x08 +#define K_9 0x09 +#define K_STANDBY 0X0C +#define K_MUTE 0X0D +#define K_VOLUMEPLUS 0X10 +#define K_VOLUMEMINUS 0X11 +#define K_ESCAPE 0X12 +#define K_YES 0X13 +#define K_NO 0X14 +#define K_BACKSPACE 0X15 +#define K_STARTVOIP 0X16 +#define K_RECEIVECALL 0X17 +#define K_HANGUP 0X18 +#define K_STAR 0X19 +#define K_SHARP 0X1A +#define K_RED 0X1B +#define K_GREEN 0X1C +#define K_BLUE 0X1D +#define K_YELLOW 0X1E +#define K_CHANNELPLUS 0X20 +#define K_CHANNELMINUS 0X21 +#define K_UP 0X22 +#define K_DOWN 0X23 +#define K_LEFT 0X24 +#define K_RIGHT 0X25 +#define K_OK 0X26 +#define K_FASTREWIND 0X32 +#define K_FASTFORWARD 0X34 +#define K_PLAYPAUSE 0X35 +#define K_STOP 0X36 +#define K_RECORDING 0X37 +#define K_PREVIOUS 0X38 +#define K_NEXT 0X39 +#define K_MENU 0X3A +#define K_MOUSE 0X3B +#define K_ALT 0X3C + +#define LAST_VALID_K K_ALT +#define K_DUMMY_RELEASE -1 + +extern void tux_user_inputs_init(void); +extern void tux_user_inputs_update_left_wing_button(void); +extern void tux_user_inputs_update_right_wing_button(void); +extern void tux_user_inputs_update_head_button(void); +extern void tux_user_inputs_update_RC5(void); +extern void tux_user_inputs_init_time_RC5(void); +extern void tux_user_inputs_update_charger_state(void); +extern bool tux_user_inputs_cmd_ir_on(void); +extern bool tux_user_inputs_cmd_ir_off(void); +extern bool tux_user_inputs_cmd_ir_send(unsigned char address, unsigned char command); + +#endif /* _TUX_USER_INPUTS_H_ */ diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..ac89951 --- /dev/null +++ b/src/version.h @@ -0,0 +1,39 @@ +/* + * Tux Droid - Version + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + /* SVN : $Id: version.h 5330 2009-09-04 11:18:09Z remi $ */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#include "svnrev.h" + +#define VER_MAJOR 0 +#define VER_MINOR 0 +#define VER_UPDATE 6 +#define VER_REVISION SVN_REV +#define VER_DATE SVN_REVDATE +#define VER_STATE " " +#ifdef WIN32 +# define VER_ARCH "WIN32" +#else +# define VER_ARCH "UNIX" +#endif + +#endif /* _VERSION_H_ */ diff --git a/test/Makefile.unix b/test/Makefile.unix new file mode 100644 index 0000000..5fb0506 --- /dev/null +++ b/test/Makefile.unix @@ -0,0 +1,60 @@ +################################################################# +## This Makefile Exported by MinGW Developer Studio +## Copyright (c) 2005 by Parinya Thipchart +################################################################# +PROJECT = test_tux_driver +CC = "/usr/bin/gcc" +OBJ_DIR = ../obj +OUTPUT_DIR = ../test +TARGET = use_tux_driver +C_INCLUDE_DIRS = +C_PREPROC = +CFLAGS = -pipe -Wall -g2 -O0 +LIB_DIRS = -L ../unix +LIBS = -ldl -ltuxdriver -lm -lpthread +LDFLAGS = -pipe -static + +SRC_OBJS = \ + $(OBJ_DIR)/main.o + + +define build_target +@echo Linking... +@$(CC) -o "$(OUTPUT_DIR)/$(TARGET)" $(SRC_OBJS) $(LIB_DIRS) $(LIBS) $(LDFLAGS) +endef + +define compile_source +@echo Compiling $< +@$(CC) $(CFLAGS) $(C_PREPROC) $(C_INCLUDE_DIRS) -c "$<" -o "$@" +endef + +.PHONY: print_header directories + +$(TARGET): print_header directories $(SRC_OBJS) + $(build_target) + +.PHONY: clean cleanall + +cleanall: + @echo Deleting intermediate files for 'test_tux_driver' + -@rm -rf "$(OBJ_DIR)" + -@rm -rf "$(OUTPUT_DIR)/$(TARGET)" + -@rmdir "$(OUTPUT_DIR)" + +clean: + @echo Deleting intermediate files for 'test_tux_driver' + -@rm -rf "$(OBJ_DIR)" + +print_header: + @echo ----------Configuration: test_tux_driver---------- + +directories: + -@if [ ! -d "$(OUTPUT_DIR)" ]; then mkdir "$(OUTPUT_DIR)"; fi + -@if [ ! -d "$(OBJ_DIR)" ]; then mkdir "$(OBJ_DIR)"; fi + +$(OBJ_DIR)/main.o: main.c \ +../include/tux_driver.h + $(compile_source) + + + diff --git a/test/Makefile.win32 b/test/Makefile.win32 new file mode 100644 index 0000000..86bb8ea --- /dev/null +++ b/test/Makefile.win32 @@ -0,0 +1,67 @@ +################################################################# +## This Makefile Exported by MinGW Developer Studio +## Copyright (c) 2002-2004 by Parinya Thipchart +################################################################# + +PROJECT = use_tux_driver +CC = "E:\MinGWStudio\MinGW\bin\gcc.exe" +OBJ_DIR = ..\obj +OUTPUT_DIR = ..\test +TARGET = use_tux_driver.exe +C_INCLUDE_DIRS = +C_PREPROC = +CFLAGS = -pipe -Wall -g2 -O0 +RC_INCLUDE_DIRS = +RC_PREPROC = +RCFLAGS = +LIB_DIRS = -L "../win32" +LIBS = -ltuxdriver +LDFLAGS = -pipe + +ifeq ($(OS),Windows_NT) + NULL = +else + NULL = nul +endif + +SRC_OBJS = \ + $(OBJ_DIR)/main.o + +define build_target +@echo Linking... +@$(CC) -o "$(OUTPUT_DIR)\$(TARGET)" $(SRC_OBJS) $(LIB_DIRS) $(LIBS) $(LDFLAGS) +endef + +define compile_source +@echo Compiling $< +@$(CC) $(CFLAGS) $(C_PREPROC) $(C_INCLUDE_DIRS) -c "$<" -o "$@" +endef + +.PHONY: print_header directories + +$(TARGET): print_header directories $(SRC_OBJS) + $(build_target) + +.PHONY: clean cleanall + +cleanall: + @echo Deleting intermediate files for 'use_tux_driver' + -@del $(OBJ_DIR)\*.o + -@del "$(OUTPUT_DIR)\$(TARGET)" + -@rmdir "$(OUTPUT_DIR)" + +clean: + @echo Deleting intermediate files for 'use_tux_driver' + -@del $(OBJ_DIR)\*.o + +print_header: + @echo ----------Configuration: use_tux_driver---------- + +directories: + -@if not exist "$(OUTPUT_DIR)\$(NULL)" mkdir "$(OUTPUT_DIR)" + -@if not exist "$(OBJ_DIR)\$(NULL)" mkdir "$(OBJ_DIR)" + +$(OBJ_DIR)/main.o: main.c \ +../include/tux_driver.h + $(compile_source) + diff --git a/test/compile.bat b/test/compile.bat new file mode 100644 index 0000000..b759b52 --- /dev/null +++ b/test/compile.bat @@ -0,0 +1,4 @@ +E: +cd MinGWStudio\MinGW\bin +mingw32-make.exe -C F:\Shared\Developpement\Boulot\tuxdroid_cross_dev\tux_driver\test -f F:\Shared\Developpement\Boulot\tuxdroid_cross_dev\tux_driver\test\Makefile.win32 +cmd \ No newline at end of file diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..c85e51a --- /dev/null +++ b/test/main.c @@ -0,0 +1,223 @@ +/* + * Tux Droid - Test driver + * Copyright (C) 2008 C2ME Sa + * + * 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, 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. + */ + +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +# include +#endif + +#include "../include/tux_driver.h" + +static int print_descriptor = 0; +static int print_status = 0; + +/** + * + */ +static void +on_status_event(status) +char *status; +{ + drv_tokens_t tokens; + tux_descriptor_t tux_desc; + char status_state[1024]; + char status_name[256]; + int status_id; + TuxDrvError err; + char *macro_str = + "0.0:TUX_CMD:MOUTH:OPEN\n" + "2.0:TUX_CMD:MOUTH:CLOSE\n"; + char *tracks = + "tada.wav|tada.wav|tada.wav|tada.wav|tada.wav|" + "tada.wav|tada.wav|tada.wav|tada.wav|tada.wav"; + + /* Split the status string */ + TuxDrv_TokenizeStatus(status, &tokens); + + /* Head button */ + if (!strcmp(tokens[0], "head_button")) + /* TuxDrv_PerformMacroFile */ + if (!strcmp(tokens[2], "True")) { + err = TuxDrv_PerformMacroFile("test_macro.txt"); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + } + + /* Left wing button */ + if (!strcmp(tokens[0], "left_wing_button")) + /* TuxDrv_PerformMacroText */ + if (!strcmp(tokens[2], "True")) { + err = TuxDrv_PerformMacroText(macro_str); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + } + + /* Right wing button */ + if (!strcmp(tokens[0], "right_wing_button")) + /* TuxDrv_PerformCommand */ + if (!strcmp(tokens[2], "True")) { + err = TuxDrv_PerformCommand(0.0, "TUX_CMD:MOUTH:ON:1,NDEF"); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + } + + /* Remote controller button */ + if (!strcmp(tokens[0], "remote_button")) { + /* K_0 */ + /* TuxDrv_SoundReflash */ + if (!strcmp(tokens[2], "K_0")) { + err = TuxDrv_SoundReflash(tracks); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + /* K_1 */ + /* TuxDrv_GetStatusName */ + }else if (!strcmp(tokens[2], "K_1")) { + err = TuxDrv_GetStatusName(SW_ID_MOUTH_POSITION, status_name); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + else + printf("The name of the status number %d is : %s\n", + SW_ID_MOUTH_POSITION, status_name); + /* K_2 */ + /* TuxDrv_GetStatusId */ + } else if (!strcmp(tokens[2], "K_2")) { + err = TuxDrv_GetStatusId("mouth_position", &status_id); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + else + printf("The number of the status (mouth_position) is : %d\n", + status_id); + /* K_3 */ + /* TuxDrv_GetStatusState */ + } else if (!strcmp(tokens[2], "K_3")) { + err = TuxDrv_GetStatusState(SW_ID_MOUTH_POSITION, status_state); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + else + printf("The status state of (mouth_position) is : %s\n", + status_state); + /* K_4 */ + /* TuxDrv_PerformCommand */ + } else if (!strcmp(tokens[2], "K_4")) { + err = TuxDrv_PerformCommand(0.0, "TUX_CMD:AUDIO:MUTE:True"); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + /* K_5 */ + /* TuxDrv_PerformCommand */ + } else if (!strcmp(tokens[2], "K_5")) { + err = TuxDrv_PerformCommand(0.0, "TUX_CMD:AUDIO:MUTE:False"); + if (err != E_TUXDRV_NOERROR) + printf("%s\n", TuxDrv_StrError(err)); + } + } + + if (print_descriptor) + { + /* Tuxdroid descriptor is complete */ + if (!strcmp(tokens[0], "descriptor_complete")) + { + /* TuxDrv_GetDescriptor */ + if (!strcmp(tokens[2], "True")) + { + TuxDrv_GetDescriptor(&tux_desc); + printf("%s\n", tux_desc.firmwares.package->version_string); + printf("%s\n", tux_desc.firmwares.tuxcore->version_string); + printf("%s\n", tux_desc.firmwares.tuxaudio->version_string); + printf("%s\n", tux_desc.firmwares.tuxrf->version_string); + printf("%s\n", tux_desc.firmwares.fuxrf->version_string); + printf("%s\n", tux_desc.firmwares.fuxusb->version_string); + printf("%s\n", tux_desc.driver.version_string); + } + } + } + + if (print_status) + { + + /* Print status string */ + if ((print_status > 1) || (strcmp(tokens[0], "light_level"))) + { + printf("%s\n", status); + } + } +} + +/** + * + */ +static void +on_end_cycle() +{ +} + +static void +usage(char *progname) +{ + fprintf(stderr, "Usage: %s [-d -s -S]\n", progname); + fprintf(stderr, " -d: prints descriptor\n"); + fprintf(stderr, " -s: prints status messages except light_sensor\n"); + fprintf(stderr, " -S: prints all status messages\n"); +} + +/** + * Main function. + */ +int +main(argc, argv) +int argc; +char* argv[]; +{ + char ch; + char *progname = argv[0]; + + // option names are to be reconsidered + while ((ch = getopt(argc, argv, "dsS")) != -1) + { + switch (ch) { + case 'd': + print_descriptor = 1; + break; + case 's': + print_status = 1; + break; + case 'S': + print_status = 2; + break; + case '?': + default: + usage(progname); + } + } + argc -= optind; + argv += optind; + + TuxDrv_SetLogLevel(LOG_LEVEL_DEBUG); + TuxDrv_SetLogTarget(LOG_TARGET_SHELL); + TuxDrv_SetStatusCallback(on_status_event); + TuxDrv_SetEndCycleCallback(on_end_cycle); + TuxDrv_Start(); + + return 0; +} diff --git a/test/tada.wav b/test/tada.wav new file mode 100644 index 0000000000000000000000000000000000000000..65ffbdce4b1635bfc2713525d8964843bc44e82c GIT binary patch literal 2736 zcmb_eTW=e=752Ured<4Ge@Y9qyYL3s)|*9k?F6o6Sb|3o2SacPVumxEGj|ZgU<8ez z5hTNkjcjZOPE;Fl;w*yfL(%`$Gx8$8F|EDK^ z>5(7*@Vm9OwJ-4X$KS85-e0W!W^J$YV<$xp{7)l_Vtn0D!%zi*ufiYz6+sfEahyak z+@i<{!@!q;P#!3V;w;INEWR^FH1K^P0~w%25f@pO#tGb+tH5&w2Uo^Wq$bx{lHBX_ z5uzXzfJw{D zs9;p;3IT`|s6cY3pFRVx&}O1*ofKh;1}YHDB3%w%T~>|Js25Vm!S^ZI3pY{5t8{LH zLdL1l8l+1|M?5VJETPO8gh@5j+83PIUFg7(cdWxi1aVo_8U!kLEcxAQqn4fz!mY+t zlM0U$(%am6ugxh?!FYK(nU*%?w&hsgzL~?g?r@nU?;6ON?HsIrV zxDR4;F#Gu|2aosnwl>bLXN)A)Q}5){VI*BH(}FXPbslYuFJ6Wsmc8xb`a?~HWZPeF9$YTC7kRzC(TBIXo!eX={Ir}n9*DjE z?Bg;bT++>Tak3oQULce-Xqqx0AE?6Vl<(|9X3Rbu4T*3Q2HueQ0k}U>lFii&v`8l9ck<^-I2O zf&c80e|Ba1127&l7jNRujvG6Vdd>B7+7%||u()i<<{*aezk}rkbo*%q!n{6%t!+Oa ztXuP+XKYUlbpZ48day~reX^4-mzlK>RjSPK0y-V0`dd!(b`f@+%%pL>sNK#UclUbg zcvWf7NtcApi&@60 z09}p8M$y5b-zBO(nj4q0P}faufb}h_I|!=9VJRKjPL509);@AsJUyO9)RD!guB%)U zi#UDal?U@uaSxpis{#ZW5YlHbnl}cY9MEM|<^dyw3smE37IfPII=oGa zmX6)VZPO|!YikiEww_N4k20B7O*1M2W>X;;!Gw+H6CH3KB;{~arU7T@iQQxpiy~d^ zF@p?`EL{V6ZvR#Qni8_mL2CxCAS%zrXqHPOxeTeg-KsZF(6`r>6E$-e} zw`ss=5GQGxU~3O?wB|0ZBQ1*b#yV#={Qvs?H%x*` Ai2wiq literal 0 HcmV?d00001 diff --git a/test/test_macro.txt b/test/test_macro.txt new file mode 100644 index 0000000..d2dcdcd --- /dev/null +++ b/test/test_macro.txt @@ -0,0 +1,9 @@ +# ----------------- Macro -------------------- # +1.0:TUX_CMD:LED:OFF:LED_BOTH +2.0:TUX_CMD:LED:SET:LED_BOTH,1.0,GRADIENT_DELTA,3.0,30 +1.0:TUX_CMD:SOUND_FLASH:PLAY:1:100 +1.0:TUX_CMD:FLIPPERS:ON_DURING:2.0,DOWN +4.0:TUX_CMD:EYES:ON_DURING:2.0,OPEN +7.0:TUX_CMD:MOUTH:ON_DURING:2.0,CLOSE +10.0:TUX_CMD:SPINNING:LEFT_ON:1 +12.0:TUX_CMD:SPINNING:RIGHT_ON:5 diff --git a/unix/Makefile b/unix/Makefile new file mode 100644 index 0000000..7509f47 --- /dev/null +++ b/unix/Makefile @@ -0,0 +1,78 @@ +PROJECT = libtuxdriver +CC = gcc +OBJ_DIR = ../obj +SRC_DIR = ../src +OUTPUT_DIR = ./ +TARGET = libtuxdriver.so +C_INCLUDE_DIRS = -I"/usr/local/include" +C_PREPROC = +CFLAGS = -pipe -std=gnu99 -DUSE_MUTEX -Wall -g0 -O2 -fPIC +RC_INCLUDE_DIRS = +RC_PREPROC = +RCFLAGS = +LIB_DIRS = +LIBS = -lpthread -lm +LDFLAGS = -pipe -shared + +SRC_OBJS = \ + $(OBJ_DIR)/tux_battery.o \ + $(OBJ_DIR)/tux_cmd_parser.o \ + $(OBJ_DIR)/tux_driver.o \ + $(OBJ_DIR)/tux_error.o \ + $(OBJ_DIR)/tux_eyes.o \ + $(OBJ_DIR)/tux_firmware.o \ + $(OBJ_DIR)/tux_hid_unix.o \ + $(OBJ_DIR)/tux_hw_status.o \ + $(OBJ_DIR)/tux_id.o \ + $(OBJ_DIR)/tux_leds.o \ + $(OBJ_DIR)/tux_light.o \ + $(OBJ_DIR)/tux_misc.o \ + $(OBJ_DIR)/tux_mouth.o \ + $(OBJ_DIR)/tux_movements.o \ + $(OBJ_DIR)/tux_pong.o \ + $(OBJ_DIR)/tux_sound_flash.o \ + $(OBJ_DIR)/tux_audio.o \ + $(OBJ_DIR)/tux_spinning.o \ + $(OBJ_DIR)/tux_descriptor.o \ + $(OBJ_DIR)/tux_sw_status.o \ + $(OBJ_DIR)/tux_usb.o \ + $(OBJ_DIR)/tux_user_inputs.o \ + $(OBJ_DIR)/tux_flippers.o \ + $(OBJ_DIR)/log.o + +.PHONY: make clean + +make: + -@if [ ! -d "$(OBJ_DIR)" ]; then mkdir "$(OBJ_DIR)"; fi + -@if [ ! -d "$(OUTPUT_DIR)" ]; then mkdir "$(OUTPUT_DIR)"; fi + -@svnwcrev $(SRC_DIR) $(SRC_DIR)/svnrev.tmpl.h $(SRC_DIR)/svnrev.h + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_battery.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_battery.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_cmd_parser.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_cmd_parser.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_driver.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_driver.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_error.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_error.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_eyes.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_eyes.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_firmware.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_firmware.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_hid_unix.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_hid_unix.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_hw_status.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_hw_status.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_id.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_id.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_leds.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_leds.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_light.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_light.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_misc.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_misc.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_mouth.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_mouth.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_movements.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_movements.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_pong.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_pong.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_sound_flash.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_sound_flash.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_audio.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_audio.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_spinning.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_spinning.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_descriptor.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_descriptor.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_sw_status.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_sw_status.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_usb.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_usb.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_user_inputs.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_user_inputs.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_flippers.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_flippers.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/log.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/log.o + $(CC) -o "$(OUTPUT_DIR)$(TARGET)" $(SRC_OBJS) $(LIB_DIRS) $(LIBS) $(LDFLAGS) + -@ar rcs $(OUTPUT_DIR)/libtuxdriver.a $(SRC_OBJS) + -@rm -fR $(OBJ_DIR)/*.o + +clean: + -@rm -fR $(OBJ_DIR)/*.o diff --git a/win32/Makefile b/win32/Makefile new file mode 100644 index 0000000..38f91bd --- /dev/null +++ b/win32/Makefile @@ -0,0 +1,82 @@ +# +# You can use this makefile in the MSYS environement with MinGW installed. +# This soft need the windows DDK. +# Please change the "C_INCLUDE_DIRS" variable with you DDK path. +# +PROJECT = libtuxdriver +CC = gcc +OBJ_DIR = ../obj +SRC_DIR = ../src +OUTPUT_DIR = ./ +TARGET = libtuxdriver.dll +C_INCLUDE_DIRS = -I "C:/Program Files (x86)/MinGWStudio/MinGW/include/ddk" +C_PREPROC = +CFLAGS = -pipe -DUSE_MUTEX -Wall -g2 -O0 +RC_INCLUDE_DIRS = +RC_PREPROC = +RCFLAGS = +LIB_DIRS = +LIBS = -lwinmm -lhid -lsetupapi -lhidparse +LDFLAGS = -pipe -shared -Wl,--output-def,"$(OUTPUT_DIR)\libtuxdriver.def",--out-implib,"$(OUTPUT_DIR)\libtuxdriver.a" -s + +SRC_OBJS = \ + $(OBJ_DIR)/tux_battery.o \ + $(OBJ_DIR)/tux_cmd_parser.o \ + $(OBJ_DIR)/tux_driver.o \ + $(OBJ_DIR)/tux_error.o \ + $(OBJ_DIR)/tux_eyes.o \ + $(OBJ_DIR)/tux_firmware.o \ + $(OBJ_DIR)/tux_hid_win32.o \ + $(OBJ_DIR)/tux_hw_status.o \ + $(OBJ_DIR)/tux_id.o \ + $(OBJ_DIR)/tux_leds.o \ + $(OBJ_DIR)/tux_light.o \ + $(OBJ_DIR)/tux_misc.o \ + $(OBJ_DIR)/tux_mouth.o \ + $(OBJ_DIR)/tux_movements.o \ + $(OBJ_DIR)/tux_pong.o \ + $(OBJ_DIR)/tux_sound_flash.o \ + $(OBJ_DIR)/tux_audio.o \ + $(OBJ_DIR)/tux_spinning.o \ + $(OBJ_DIR)/tux_descriptor.o \ + $(OBJ_DIR)/tux_sw_status.o \ + $(OBJ_DIR)/tux_usb.o \ + $(OBJ_DIR)/tux_user_inputs.o \ + $(OBJ_DIR)/tux_flippers.o \ + $(OBJ_DIR)/log.o + +.PHONY: make clean + +make: + -@if [ ! -d "$(OBJ_DIR)" ]; then mkdir "$(OBJ_DIR)"; fi + -@if [ ! -d "$(OUTPUT_DIR)" ]; then mkdir "$(OUTPUT_DIR)"; fi + -@SubWCRev $(SRC_DIR) $(SRC_DIR)/svnrev.tmpl.h $(SRC_DIR)/svnrev.h + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_battery.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_battery.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_cmd_parser.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_cmd_parser.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_driver.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_driver.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_error.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_error.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_eyes.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_eyes.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_firmware.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_firmware.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_hid_win32.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_hid_win32.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_hw_status.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_hw_status.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_id.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_id.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_leds.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_leds.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_light.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_light.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_misc.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_misc.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_mouth.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_mouth.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_movements.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_movements.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_pong.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_pong.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_sound_flash.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_sound_flash.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_audio.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_audio.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_spinning.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_spinning.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_descriptor.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_descriptor.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_sw_status.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_sw_status.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_usb.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_usb.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_user_inputs.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_user_inputs.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/tux_flippers.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/tux_flippers.o + $(CC) -c $(CFLAGS) $(SRC_DIR)/log.c $(C_INCLUDE_DIRS) -o $(OBJ_DIR)/log.o + $(CC) -o "$(OUTPUT_DIR)\$(TARGET)" $(SRC_OBJS) $(LIB_DIRS) $(LIBS) $(LDFLAGS) + -@rm -fR $(OBJ_DIR)/*.o + +clean: + -@rm -fR $(OBJ_DIR)/*.o \ No newline at end of file diff --git a/win32/libtuxdriver.dev b/win32/libtuxdriver.dev new file mode 100644 index 0000000..2ec826e --- /dev/null +++ b/win32/libtuxdriver.dev @@ -0,0 +1,602 @@ +[Project] +FileName=libtuxdriver.dev +Name=libtuxdriver +Type=1 +Ver=2 +ObjFiles= +Includes= +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler= +Linker= +IsCpp=0 +Icon= +ExeOutput= +ObjectOutput= +LogOutput= +LogOutputEnabled=0 +OverrideOutput=0 +OverrideOutputName= +HostApplication= +Folders=headers,sources +CommandLine= +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000000000000000000000 +UnitCount=55 + +[VersionInfo] +Major=1 +Minor=0 +Release=0 +Build=0 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 +SyncProduct=1 + +[Unit1] +FileName=..\include\tux_driver.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=..\src\log.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit3] +FileName=..\src\svnrev.tmpl.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=..\src\threading_uniform.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=..\src\tux_audio.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=..\src\tux_battery.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit7] +FileName=..\src\tux_cmd_parser.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit8] +FileName=..\src\tux_descriptor.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit9] +FileName=..\src\tux_error.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit10] +FileName=..\src\tux_eyes.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=..\src\tux_firmware.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit12] +FileName=..\src\tux_flippers.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit13] +FileName=..\src\tux_hid_unix.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit14] +FileName=..\src\tux_hid_win32.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit15] +FileName=..\src\tux_hw_cmd.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit16] +FileName=..\src\tux_hw_status.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit17] +FileName=..\src\tux_id.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit18] +FileName=..\src\tux_leds.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit19] +FileName=..\src\tux_light.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit20] +FileName=..\src\tux_misc.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit21] +FileName=..\src\tux_mouth.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit22] +FileName=..\src\tux_movements.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit23] +FileName=..\src\tux_pong.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit24] +FileName=..\src\tux_sound_flash.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit25] +FileName=..\src\tux_spinning.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit26] +FileName=..\src\tux_sw_status.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit27] +FileName=..\src\tux_types.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit28] +FileName=..\src\tux_usb.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit29] +FileName=..\src\tux_user_inputs.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit30] +FileName=..\src\version.h +CompileCpp=0 +Folder=headers +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit31] +FileName=..\src\log.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit32] +FileName=..\src\tux_audio.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit33] +FileName=..\src\tux_battery.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit34] +FileName=..\src\tux_cmd_parser.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit35] +FileName=..\src\tux_descriptor.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit36] +FileName=..\src\tux_driver.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit37] +FileName=..\src\tux_error.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit38] +FileName=..\src\tux_eyes.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit39] +FileName=..\src\tux_firmware.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit40] +FileName=..\src\tux_flippers.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit41] +FileName=..\src\tux_hid_unix.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit42] +FileName=..\src\tux_hid_win32.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit43] +FileName=..\src\tux_hw_status.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit44] +FileName=..\src\tux_id.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit45] +FileName=..\src\tux_leds.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit46] +FileName=..\src\tux_light.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit47] +FileName=..\src\tux_misc.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit48] +FileName=..\src\tux_mouth.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit49] +FileName=..\src\tux_movements.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit50] +FileName=..\src\tux_pong.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit51] +FileName=..\src\tux_sound_flash.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] +FileName=..\src\tux_spinning.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit53] +FileName=..\src\tux_sw_status.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit54] +FileName=..\src\tux_usb.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] +FileName=..\src\tux_user_inputs.c +CompileCpp=0 +Folder=sources +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= +