diff --git a/plugins/USBqemu/License-qemu.txt b/plugins/USBqemu/License-qemu.txt new file mode 100644 index 0000000000..bf51c07090 --- /dev/null +++ b/plugins/USBqemu/License-qemu.txt @@ -0,0 +1,23 @@ +/* + * QEMU USB emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ diff --git a/plugins/USBqemu/License.txt b/plugins/USBqemu/License.txt new file mode 100644 index 0000000000..bb0a0ce0eb --- /dev/null +++ b/plugins/USBqemu/License.txt @@ -0,0 +1,342 @@ + 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/plugins/USBqemu/PS2Edefs.h b/plugins/USBqemu/PS2Edefs.h new file mode 100644 index 0000000000..fbb0c2eb06 --- /dev/null +++ b/plugins/USBqemu/PS2Edefs.h @@ -0,0 +1,809 @@ +#ifndef __PS2EDEFS_H__ +#define __PS2EDEFS_H__ + +/* + * PS2E Definitions v0.6.2 (beta) + * + * Author: linuzappz@hotmail.com + * shadowpcsx2@yahoo.gr + * florinsasu@hotmail.com + */ + +/* + Notes: + * Since this is still beta things may change. + + * OSflags: + __LINUX__ (linux OS) + __WIN32__ (win32 OS) + + * common return values (for ie. GSinit): + 0 - success + -1 - error + + * reserved keys: + F1 to F10 are reserved for the emulator + + * plugins should NOT change the current + working directory. + (on win32, add flag OFN_NOCHANGEDIR for + GetOpenFileName) + +*/ + +#include "PS2Etypes.h" + +#ifdef __LINUX__ +#define CALLBACK +#else +#include +#endif + +/* common defines */ + +#if defined(GSdefs) || defined(PADdefs) || defined(SIOdefs) || \ + defined(SPU2defs) || defined(CDVDdefs) || defined(DEV9defs) || \ + defined(USBdefs) || defined(FWdefs) +#define COMMONdefs +#endif + +// PS2EgetLibType returns (may be OR'd) +#define PS2E_LT_GS 0x01 +#define PS2E_LT_PAD 0x02 // -=[ OBSOLETE ]=- +#define PS2E_LT_SPU2 0x04 +#define PS2E_LT_CDVD 0x08 +#define PS2E_LT_DEV9 0x10 +#define PS2E_LT_USB 0x20 +#define PS2E_LT_FW 0x40 +#define PS2E_LT_SIO 0x80 + +// PS2EgetLibVersion2 (high 16 bits) +#define PS2E_GS_VERSION 0x0006 +#define PS2E_PAD_VERSION 0x0002 // -=[ OBSOLETE ]=- +#define PS2E_SPU2_VERSION 0x0005 +#define PS2E_CDVD_VERSION 0x0005 +#define PS2E_DEV9_VERSION 0x0003 +#define PS2E_USB_VERSION 0x0003 +#define PS2E_FW_VERSION 0x0002 +#define PS2E_SIO_VERSION 0x0001 +#ifdef COMMONdefs + +u32 CALLBACK PS2EgetLibType(void); +u32 CALLBACK PS2EgetLibVersion2(u32 type); +char* CALLBACK PS2EgetLibName(void); + +#endif + +// key values: +/* key values must be OS dependant: + win32: the VK_XXX will be used (WinUser) + linux: the XK_XXX will be used (XFree86) +*/ + +// event values: +#define KEYPRESS 1 +#define KEYRELEASE 2 + +typedef struct { + u32 key; + u32 event; +} keyEvent; + +// plugin types +#define SIO_TYPE_PAD 0x00000001 +#define SIO_TYPE_MTAP 0x00000004 +#define SIO_TYPE_RM 0x00000040 +#define SIO_TYPE_MC 0x00000100 + +typedef int (CALLBACK * SIOchangeSlotCB)(int slot); + +typedef struct { + u8 ctrl:4; // control and mode bits + u8 mode:4; // control and mode bits + u8 trackNum; // current track number (1 to 99) + u8 trackIndex; // current index within track (0 to 99) + u8 trackM; // current minute location on the disc (BCD encoded) + u8 trackS; // current sector location on the disc (BCD encoded) + u8 trackF; // current frame location on the disc (BCD encoded) + u8 pad; // unused + u8 discM; // current minute offset from first track (BCD encoded) + u8 discS; // current sector offset from first track (BCD encoded) + u8 discF; // current frame offset from first track (BCD encoded) +} cdvdSubQ; + +typedef struct { // NOT bcd coded + u32 lsn; + u8 type; +} cdvdTD; + +typedef struct { + u8 strack; //number of the first track (usually 1) + u8 etrack; //number of the last track +} cdvdTN; + +// CDVDreadTrack mode values: +#define CDVD_MODE_2352 0 // full 2352 bytes +#define CDVD_MODE_2340 1 // skip sync (12) bytes +#define CDVD_MODE_2328 2 // skip sync+head+sub (24) bytes +#define CDVD_MODE_2048 3 // skip sync+head+sub (24) bytes +#define CDVD_MODE_2368 4 // full 2352 bytes + 16 subq + +// CDVDgetDiskType returns: +#define CDVD_TYPE_ILLEGAL 0xff // Illegal Disc +#define CDVD_TYPE_DVDV 0xfe // DVD Video +#define CDVD_TYPE_CDDA 0xfd // Audio CD +#define CDVD_TYPE_PS2DVD 0x14 // PS2 DVD +#define CDVD_TYPE_PS2CDDA 0x13 // PS2 CD (with audio) +#define CDVD_TYPE_PS2CD 0x12 // PS2 CD +#define CDVD_TYPE_PSCDDA 0x11 // PS CD (with audio) +#define CDVD_TYPE_PSCD 0x10 // PS CD +#define CDVD_TYPE_UNKNOWN 0x05 // Unknown +#define CDVD_TYPE_DETCTDVDD 0x04 // Detecting Dvd Dual Sided +#define CDVD_TYPE_DETCTDVDS 0x03 // Detecting Dvd Single Sided +#define CDVD_TYPE_DETCTCD 0x02 // Detecting Cd +#define CDVD_TYPE_DETCT 0x01 // Detecting +#define CDVD_TYPE_NODISC 0x00 // No Disc + +// CDVDgetTrayStatus returns: +#define CDVD_TRAY_CLOSE 0x00 +#define CDVD_TRAY_OPEN 0x01 + +// cdvdTD.type (track types for cds) +#define CDVD_AUDIO_TRACK 0x01 +#define CDVD_MODE1_TRACK 0x41 +#define CDVD_MODE2_TRACK 0x61 + +#define CDVD_AUDIO_MASK 0x00 +#define CDVD_DATA_MASK 0x40 +// CDROM_DATA_TRACK 0x04 //do not enable this! (from linux kernel) + +typedef void (*DEV9callback)(int cycles); +typedef int (*DEV9handler)(void); + +typedef void (*USBcallback)(int cycles); +typedef int (*USBhandler)(void); + +// freeze modes: +#define FREEZE_LOAD 0 +#define FREEZE_SAVE 1 +#define FREEZE_SIZE 2 + +typedef struct { + char name[8]; + void *common; +} GSdriverInfo; + +#ifdef __WIN32__ +typedef struct { // unsupported values must be set to zero + HWND hWnd; + HMENU hMenu; + HWND hStatusWnd; +} winInfo; +#endif + +/* GS plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef GSdefs + +// basic funcs + +s32 CALLBACK GSinit(); +s32 CALLBACK GSopen(void *pDsp, char *Title, int multithread); +void CALLBACK GSclose(); +void CALLBACK GSshutdown(); +void CALLBACK GSvsync(int field); +void CALLBACK GSgifTransfer1(u32 *pMem, u32 addr); +void CALLBACK GSgifTransfer2(u32 *pMem, u32 size); +void CALLBACK GSgifTransfer3(u32 *pMem, u32 size); +void CALLBACK GSgifSoftReset(u32 mask); +void CALLBACK GSreadFIFO(u64 *mem); +void CALLBACK GSreadFIFO2(u64 *mem, int qwc); + +// extended funcs + +// GSkeyEvent gets called when there is a keyEvent from the PAD plugin +void CALLBACK GSkeyEvent(keyEvent *ev); +void CALLBACK GSchangeSaveState(int, const char* filename); +void CALLBACK GSmakeSnapshot(char *path); +void CALLBACK GSmakeSnapshot2(char *pathname, int* snapdone, int savejpg); +void CALLBACK GSirqCallback(void (*callback)()); +void CALLBACK GSprintf(int timeout, char *fmt, ...); +void CALLBACK GSsetBaseMem(void*); +void CALLBACK GSsetGameCRC(int); + +// controls frame skipping in the GS, if this routine isn't present, frame skipping won't be done +void CALLBACK GSsetFrameSkip(int frameskip); + +void CALLBACK GSreset(); +void CALLBACK GSwriteCSR(u32 value); +void CALLBACK GSgetDriverInfo(GSdriverInfo *info); +#ifdef __WIN32__ +s32 CALLBACK GSsetWindowInfo(winInfo *info); +#endif +s32 CALLBACK GSfreeze(int mode, freezeData *data); +void CALLBACK GSconfigure(); +void CALLBACK GSabout(); +s32 CALLBACK GStest(); + +#endif + +/* PAD plugin API -=[ OBSOLETE ]=- */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef PADdefs + +// basic funcs + +s32 CALLBACK PADinit(u32 flags); +s32 CALLBACK PADopen(void *pDsp); +void CALLBACK PADclose(); +void CALLBACK PADshutdown(); +// PADkeyEvent is called every vsync (return NULL if no event) +keyEvent* CALLBACK PADkeyEvent(); +u8 CALLBACK PADstartPoll(int pad); +u8 CALLBACK PADpoll(u8 value); +// returns: 1 if supported pad1 +// 2 if supported pad2 +// 3 if both are supported +u32 CALLBACK PADquery(); + +// extended funcs + +void CALLBACK PADgsDriverInfo(GSdriverInfo *info); +void CALLBACK PADconfigure(); +void CALLBACK PADabout(); +s32 CALLBACK PADtest(); + +#endif + +/* SIO plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef SIOdefs + +// basic funcs + +s32 CALLBACK SIOinit(u32 port, u32 slot, SIOchangeSlotCB f); +s32 CALLBACK SIOopen(void *pDsp); +void CALLBACK SIOclose(); +void CALLBACK SIOshutdown(); +u8 CALLBACK SIOstartPoll(u8 value); +u8 CALLBACK SIOpoll(u8 value); +// returns: SIO_TYPE_{PAD,MTAP,RM,MC} +u32 CALLBACK SIOquery(); + +// extended funcs + +void CALLBACK SIOconfigure(); +void CALLBACK SIOabout(); +s32 CALLBACK SIOtest(); + +#endif + +/* SPU2 plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef SPU2defs + +// basic funcs + +s32 CALLBACK SPU2init(); +s32 CALLBACK SPU2open(void *pDsp); +void CALLBACK SPU2close(); +void CALLBACK SPU2shutdown(); +void CALLBACK SPU2write(u32 mem, u16 value); +u16 CALLBACK SPU2read(u32 mem); +void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size); +void CALLBACK SPU2writeDMA4Mem(u16 *pMem, int size); +void CALLBACK SPU2interruptDMA4(); +void CALLBACK SPU2readDMA7Mem(u16* pMem, int size); +void CALLBACK SPU2writeDMA7Mem(u16 *pMem, int size); +void CALLBACK SPU2interruptDMA7(); +u32 CALLBACK SPU2ReadMemAddr(int core); +void CALLBACK SPU2WriteMemAddr(int core,u32 value); +void CALLBACK SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); +// extended funcs + +void CALLBACK SPU2async(u32 cycles); +s32 CALLBACK SPU2freeze(int mode, freezeData *data); +void CALLBACK SPU2configure(); +void CALLBACK SPU2about(); +s32 CALLBACK SPU2test(); + +#endif + +/* CDVD plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef CDVDdefs + +// basic funcs + +s32 CALLBACK CDVDinit(); +s32 CALLBACK CDVDopen(); +void CALLBACK CDVDclose(); +void CALLBACK CDVDshutdown(); +s32 CALLBACK CDVDreadTrack(u32 lsn, int mode); + +// return can be NULL (for async modes) +u8* CALLBACK CDVDgetBuffer(); + +s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq);//read subq from disc (only cds have subq data) +s32 CALLBACK CDVDgetTN(cdvdTN *Buffer); //disk information +s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer); //track info: min,sec,frame,type +s32 CALLBACK CDVDgetTOC(void* toc); //gets ps2 style toc from disc +s32 CALLBACK CDVDgetDiskType(); //CDVD_TYPE_xxxx +s32 CALLBACK CDVDgetTrayStatus(); //CDVD_TRAY_xxxx +s32 CALLBACK CDVDctrlTrayOpen(); //open disc tray +s32 CALLBACK CDVDctrlTrayClose(); //close disc tray + +// extended funcs + +void CALLBACK CDVDconfigure(); +void CALLBACK CDVDabout(); +s32 CALLBACK CDVDtest(); +void CALLBACK CDVDnewDiskCB(void (*callback)()); + +#endif + +/* DEV9 plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef DEV9defs + +// basic funcs + +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +s32 CALLBACK DEV9init(); +s32 CALLBACK DEV9open(void *pDsp); +void CALLBACK DEV9close(); +void CALLBACK DEV9shutdown(); +u8 CALLBACK DEV9read8(u32 addr); +u16 CALLBACK DEV9read16(u32 addr); +u32 CALLBACK DEV9read32(u32 addr); +void CALLBACK DEV9write8(u32 addr, u8 value); +void CALLBACK DEV9write16(u32 addr, u16 value); +void CALLBACK DEV9write32(u32 addr, u32 value); +void CALLBACK DEV9readDMA8Mem(u32 *pMem, int size); +void CALLBACK DEV9writeDMA8Mem(u32 *pMem, int size); +// cycles = IOP cycles before calling callback, +// if callback returns 1 the irq is triggered, else not +void CALLBACK DEV9irqCallback(DEV9callback callback); +DEV9handler CALLBACK DEV9irqHandler(void); + +// extended funcs + +s32 CALLBACK DEV9freeze(int mode, freezeData *data); +void CALLBACK DEV9configure(); +void CALLBACK DEV9about(); +s32 CALLBACK DEV9test(); + +#endif + +/* USB plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef USBdefs + +// basic funcs + +s32 CALLBACK USBinit(); +s32 CALLBACK USBopen(void *pDsp); +void CALLBACK USBclose(); +void CALLBACK USBshutdown(); +u8 CALLBACK USBread8(u32 addr); +u16 CALLBACK USBread16(u32 addr); +u32 CALLBACK USBread32(u32 addr); +void CALLBACK USBwrite8(u32 addr, u8 value); +void CALLBACK USBwrite16(u32 addr, u16 value); +void CALLBACK USBwrite32(u32 addr, u32 value); +void CALLBACK USBasync(u32 cycles); + +// cycles = IOP cycles before calling callback, +// if callback returns 1 the irq is triggered, else not +void CALLBACK USBirqCallback(USBcallback callback); +USBhandler CALLBACK USBirqHandler(void); +void CALLBACK USBsetRAM(void *mem); + +// extended funcs + +s32 CALLBACK USBfreeze(int mode, freezeData *data); +void CALLBACK USBconfigure(); +void CALLBACK USBabout(); +s32 CALLBACK USBtest(); + +#endif + +/* FW plugin API */ + +// if this file is included with this define +// the next api will not be skipped by the compiler +#ifdef FWdefs +// basic funcs + +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +s32 CALLBACK FWinit(); +s32 CALLBACK FWopen(void *pDsp); +void CALLBACK FWclose(); +void CALLBACK FWshutdown(); +u32 CALLBACK FWread32(u32 addr); +void CALLBACK FWwrite32(u32 addr, u32 value); +void CALLBACK FWirqCallback(void (*callback)()); + +// extended funcs + +s32 CALLBACK FWfreeze(int mode, freezeData *data); +void CALLBACK FWconfigure(); +void CALLBACK FWabout(); +s32 CALLBACK FWtest(); +#endif + +// might be useful for emulators +#ifdef PLUGINtypedefs + +typedef u32 (CALLBACK* _PS2EgetLibType)(void); +typedef u32 (CALLBACK* _PS2EgetLibVersion2)(u32 type); +typedef char*(CALLBACK* _PS2EgetLibName)(void); + +// GS +// NOTE: GSreadFIFOX/GSwriteCSR functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _GSinit)(); +typedef s32 (CALLBACK* _GSopen)(void *pDsp, char *Title, int multithread); +typedef void (CALLBACK* _GSclose)(); +typedef void (CALLBACK* _GSshutdown)(); +typedef void (CALLBACK* _GSvsync)(int field); +typedef void (CALLBACK* _GSgifTransfer1)(u32 *pMem, u32 addr); +typedef void (CALLBACK* _GSgifTransfer2)(u32 *pMem, u32 size); +typedef void (CALLBACK* _GSgifTransfer3)(u32 *pMem, u32 size); +typedef void (CALLBACK* _GSgifSoftReset)(u32 mask); +typedef void (CALLBACK* _GSreadFIFO)(u64 *pMem); +typedef void (CALLBACK* _GSreadFIFO2)(u64 *pMem, int qwc); + +typedef void (CALLBACK* _GSkeyEvent)(keyEvent* ev); +typedef void (CALLBACK* _GSchangeSaveState)(int, const char* filename); +typedef void (CALLBACK* _GSirqCallback)(void (*callback)()); +typedef void (CALLBACK* _GSprintf)(int timeout, char *fmt, ...); +typedef void (CALLBACK* _GSsetBaseMem)(void*); +typedef void (CALLBACK* _GSsetGameCRC)(int); +typedef void (CALLBACK* _GSsetFrameSkip)(int frameskip); +typedef void (CALLBACK* _GSreset)(); +typedef void (CALLBACK* _GSwriteCSR)(u32 value); +typedef void (CALLBACK* _GSgetDriverInfo)(GSdriverInfo *info); +#ifdef __WIN32__ +typedef s32 (CALLBACK* _GSsetWindowInfo)(winInfo *info); +#endif +typedef void (CALLBACK* _GSmakeSnapshot)(char *path); +typedef void (CALLBACK* _GSmakeSnapshot2)(char *path, int*, int); +typedef s32 (CALLBACK* _GSfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _GSconfigure)(); +typedef s32 (CALLBACK* _GStest)(); +typedef void (CALLBACK* _GSabout)(); + +// PAD +typedef s32 (CALLBACK* _PADinit)(u32 flags); +typedef s32 (CALLBACK* _PADopen)(void *pDsp); +typedef void (CALLBACK* _PADclose)(); +typedef void (CALLBACK* _PADshutdown)(); +typedef keyEvent* (CALLBACK* _PADkeyEvent)(); +typedef u8 (CALLBACK* _PADstartPoll)(int pad); +typedef u8 (CALLBACK* _PADpoll)(u8 value); +typedef u32 (CALLBACK* _PADquery)(); + +typedef void (CALLBACK* _PADgsDriverInfo)(GSdriverInfo *info); +typedef void (CALLBACK* _PADconfigure)(); +typedef s32 (CALLBACK* _PADtest)(); +typedef void (CALLBACK* _PADabout)(); + +// SIO +typedef s32 (CALLBACK* _SIOinit)(u32 port, u32 slot, SIOchangeSlotCB f); +typedef s32 (CALLBACK* _SIOopen)(void *pDsp); +typedef void (CALLBACK* _SIOclose)(); +typedef void (CALLBACK* _SIOshutdown)(); +typedef u8 (CALLBACK* _SIOstartPoll)(u8 value); +typedef u8 (CALLBACK* _SIOpoll)(u8 value); +typedef u32 (CALLBACK* _SIOquery)(); + +typedef void (CALLBACK* _SIOconfigure)(); +typedef s32 (CALLBACK* _SIOtest)(); +typedef void (CALLBACK* _SIOabout)(); + +// SPU2 +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _SPU2init)(); +typedef s32 (CALLBACK* _SPU2open)(void *pDsp); +typedef void (CALLBACK* _SPU2close)(); +typedef void (CALLBACK* _SPU2shutdown)(); +typedef void (CALLBACK* _SPU2write)(u32 mem, u16 value); +typedef u16 (CALLBACK* _SPU2read)(u32 mem); +typedef void (CALLBACK* _SPU2readDMA4Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2writeDMA4Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2interruptDMA4)(); +typedef void (CALLBACK* _SPU2readDMA7Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2writeDMA7Mem)(u16 *pMem, int size); +typedef void (CALLBACK* _SPU2interruptDMA7)(); +typedef void (CALLBACK* _SPU2irqCallback)(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); +typedef u32 (CALLBACK* _SPU2ReadMemAddr)(int core); +typedef void (CALLBACK* _SPU2WriteMemAddr)(int core,u32 value); +typedef void (CALLBACK* _SPU2async)(u32 cycles); +typedef s32 (CALLBACK* _SPU2freeze)(int mode, freezeData *data); +typedef void (CALLBACK* _SPU2configure)(); +typedef s32 (CALLBACK* _SPU2test)(); +typedef void (CALLBACK* _SPU2about)(); + +// CDVD +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _CDVDinit)(); +typedef s32 (CALLBACK* _CDVDopen)(); +typedef void (CALLBACK* _CDVDclose)(); +typedef void (CALLBACK* _CDVDshutdown)(); +typedef s32 (CALLBACK* _CDVDreadTrack)(u32 lsn, int mode); +typedef u8* (CALLBACK* _CDVDgetBuffer)(); +typedef s32 (CALLBACK* _CDVDreadSubQ)(u32 lsn, cdvdSubQ* subq); +typedef s32 (CALLBACK* _CDVDgetTN)(cdvdTN *Buffer); +typedef s32 (CALLBACK* _CDVDgetTD)(u8 Track, cdvdTD *Buffer); +typedef s32 (CALLBACK* _CDVDgetTOC)(void* toc); +typedef s32 (CALLBACK* _CDVDgetDiskType)(); +typedef s32 (CALLBACK* _CDVDgetTrayStatus)(); +typedef s32 (CALLBACK* _CDVDctrlTrayOpen)(); +typedef s32 (CALLBACK* _CDVDctrlTrayClose)(); + +typedef void (CALLBACK* _CDVDconfigure)(); +typedef s32 (CALLBACK* _CDVDtest)(); +typedef void (CALLBACK* _CDVDabout)(); +typedef void (CALLBACK* _CDVDnewDiskCB)(void (*callback)()); + +// DEV9 +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _DEV9init)(); +typedef s32 (CALLBACK* _DEV9open)(void *pDsp); +typedef void (CALLBACK* _DEV9close)(); +typedef void (CALLBACK* _DEV9shutdown)(); +typedef u8 (CALLBACK* _DEV9read8)(u32 mem); +typedef u16 (CALLBACK* _DEV9read16)(u32 mem); +typedef u32 (CALLBACK* _DEV9read32)(u32 mem); +typedef void (CALLBACK* _DEV9write8)(u32 mem, u8 value); +typedef void (CALLBACK* _DEV9write16)(u32 mem, u16 value); +typedef void (CALLBACK* _DEV9write32)(u32 mem, u32 value); +typedef void (CALLBACK* _DEV9readDMA8Mem)(u32 *pMem, int size); +typedef void (CALLBACK* _DEV9writeDMA8Mem)(u32 *pMem, int size); +typedef void (CALLBACK* _DEV9irqCallback)(DEV9callback callback); +typedef DEV9handler (CALLBACK* _DEV9irqHandler)(void); + +typedef s32 (CALLBACK* _DEV9freeze)(int mode, freezeData *data); +typedef void (CALLBACK* _DEV9configure)(); +typedef s32 (CALLBACK* _DEV9test)(); +typedef void (CALLBACK* _DEV9about)(); + +// USB +// NOTE: The read/write functions CANNOT use XMM/MMX regs +// If you want to use them, need to save and restore current ones +typedef s32 (CALLBACK* _USBinit)(); +typedef s32 (CALLBACK* _USBopen)(void *pDsp); +typedef void (CALLBACK* _USBclose)(); +typedef void (CALLBACK* _USBshutdown)(); +typedef u8 (CALLBACK* _USBread8)(u32 mem); +typedef u16 (CALLBACK* _USBread16)(u32 mem); +typedef u32 (CALLBACK* _USBread32)(u32 mem); +typedef void (CALLBACK* _USBwrite8)(u32 mem, u8 value); +typedef void (CALLBACK* _USBwrite16)(u32 mem, u16 value); +typedef void (CALLBACK* _USBwrite32)(u32 mem, u32 value); +typedef void (CALLBACK* _USBirqCallback)(USBcallback callback); +typedef USBhandler (CALLBACK* _USBirqHandler)(void); +typedef void (CALLBACK* _USBsetRAM)(void *mem); +typedef void (CALLBACK* _USBasync)(u32 cycles); + +typedef s32 (CALLBACK* _USBfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _USBconfigure)(); +typedef s32 (CALLBACK* _USBtest)(); +typedef void (CALLBACK* _USBabout)(); + +//FW +typedef s32 (CALLBACK* _FWinit)(); +typedef s32 (CALLBACK* _FWopen)(void *pDsp); +typedef void (CALLBACK* _FWclose)(); +typedef void (CALLBACK* _FWshutdown)(); +typedef u32 (CALLBACK* _FWread32)(u32 mem); +typedef void (CALLBACK* _FWwrite32)(u32 mem, u32 value); +typedef void (CALLBACK* _FWirqCallback)(void (*callback)()); + +typedef s32 (CALLBACK* _FWfreeze)(int mode, freezeData *data); +typedef void (CALLBACK* _FWconfigure)(); +typedef s32 (CALLBACK* _FWtest)(); +typedef void (CALLBACK* _FWabout)(); + +#endif + +#ifdef PLUGINfuncs + +// GS +_GSinit GSinit; +_GSopen GSopen; +_GSclose GSclose; +_GSshutdown GSshutdown; +_GSvsync GSvsync; +_GSgifTransfer1 GSgifTransfer1; +_GSgifTransfer2 GSgifTransfer2; +_GSgifTransfer3 GSgifTransfer3; +_GSgifSoftReset GSgifSoftReset; +_GSreadFIFO GSreadFIFO; +_GSreadFIFO2 GSreadFIFO2; + +_GSkeyEvent GSkeyEvent; +_GSchangeSaveState GSchangeSaveState; +_GSmakeSnapshot GSmakeSnapshot; +_GSmakeSnapshot2 GSmakeSnapshot2; +_GSirqCallback GSirqCallback; +_GSprintf GSprintf; +_GSsetBaseMem GSsetBaseMem; +_GSsetGameCRC GSsetGameCRC; +_GSsetFrameSkip GSsetFrameSkip; +_GSreset GSreset; +_GSwriteCSR GSwriteCSR; +_GSgetDriverInfo GSgetDriverInfo; +#ifdef __WIN32__ +_GSsetWindowInfo GSsetWindowInfo; +#endif +_GSfreeze GSfreeze; +_GSconfigure GSconfigure; +_GStest GStest; +_GSabout GSabout; + +// PAD1 +_PADinit PAD1init; +_PADopen PAD1open; +_PADclose PAD1close; +_PADshutdown PAD1shutdown; +_PADkeyEvent PAD1keyEvent; +_PADstartPoll PAD1startPoll; +_PADpoll PAD1poll; +_PADquery PAD1query; + +_PADgsDriverInfo PAD1gsDriverInfo; +_PADconfigure PAD1configure; +_PADtest PAD1test; +_PADabout PAD1about; + +// PAD2 +_PADinit PAD2init; +_PADopen PAD2open; +_PADclose PAD2close; +_PADshutdown PAD2shutdown; +_PADkeyEvent PAD2keyEvent; +_PADstartPoll PAD2startPoll; +_PADpoll PAD2poll; +_PADquery PAD2query; + +_PADgsDriverInfo PAD2gsDriverInfo; +_PADconfigure PAD2configure; +_PADtest PAD2test; +_PADabout PAD2about; + +// SIO[2] +_SIOinit SIOinit[2][9]; +_SIOopen SIOopen[2][9]; +_SIOclose SIOclose[2][9]; +_SIOshutdown SIOshutdown[2][9]; +_SIOstartPoll SIOstartPoll[2][9]; +_SIOpoll SIOpoll[2][9]; +_SIOquery SIOquery[2][9]; + +_SIOconfigure SIOconfigure[2][9]; +_SIOtest SIOtest[2][9]; +_SIOabout SIOabout[2][9]; + +// SPU2 +_SPU2init SPU2init; +_SPU2open SPU2open; +_SPU2close SPU2close; +_SPU2shutdown SPU2shutdown; +_SPU2write SPU2write; +_SPU2read SPU2read; +_SPU2readDMA4Mem SPU2readDMA4Mem; +_SPU2writeDMA4Mem SPU2writeDMA4Mem; +_SPU2interruptDMA4 SPU2interruptDMA4; +_SPU2readDMA7Mem SPU2readDMA7Mem; +_SPU2writeDMA7Mem SPU2writeDMA7Mem; +_SPU2interruptDMA7 SPU2interruptDMA7; +_SPU2ReadMemAddr SPU2ReadMemAddr; +_SPU2WriteMemAddr SPU2WriteMemAddr; +_SPU2irqCallback SPU2irqCallback; + +_SPU2async SPU2async; +_SPU2freeze SPU2freeze; +_SPU2configure SPU2configure; +_SPU2test SPU2test; +_SPU2about SPU2about; + +// CDVD +_CDVDinit CDVDinit; +_CDVDopen CDVDopen; +_CDVDclose CDVDclose; +_CDVDshutdown CDVDshutdown; +_CDVDreadTrack CDVDreadTrack; +_CDVDgetBuffer CDVDgetBuffer; +_CDVDreadSubQ CDVDreadSubQ; +_CDVDgetTN CDVDgetTN; +_CDVDgetTD CDVDgetTD; +_CDVDgetTOC CDVDgetTOC; +_CDVDgetDiskType CDVDgetDiskType; +_CDVDgetTrayStatus CDVDgetTrayStatus; +_CDVDctrlTrayOpen CDVDctrlTrayOpen; +_CDVDctrlTrayClose CDVDctrlTrayClose; + +_CDVDconfigure CDVDconfigure; +_CDVDtest CDVDtest; +_CDVDabout CDVDabout; +_CDVDnewDiskCB CDVDnewDiskCB; + +// DEV9 +_DEV9init DEV9init; +_DEV9open DEV9open; +_DEV9close DEV9close; +_DEV9shutdown DEV9shutdown; +_DEV9read8 DEV9read8; +_DEV9read16 DEV9read16; +_DEV9read32 DEV9read32; +_DEV9write8 DEV9write8; +_DEV9write16 DEV9write16; +_DEV9write32 DEV9write32; +_DEV9readDMA8Mem DEV9readDMA8Mem; +_DEV9writeDMA8Mem DEV9writeDMA8Mem; +_DEV9irqCallback DEV9irqCallback; +_DEV9irqHandler DEV9irqHandler; + +_DEV9configure DEV9configure; +_DEV9freeze DEV9freeze; +_DEV9test DEV9test; +_DEV9about DEV9about; + +// USB +_USBinit USBinit; +_USBopen USBopen; +_USBclose USBclose; +_USBshutdown USBshutdown; +_USBread8 USBread8; +_USBread16 USBread16; +_USBread32 USBread32; +_USBwrite8 USBwrite8; +_USBwrite16 USBwrite16; +_USBwrite32 USBwrite32; +_USBirqCallback USBirqCallback; +_USBirqHandler USBirqHandler; +_USBsetRAM USBsetRAM; +_USBasync USBasync; + +_USBconfigure USBconfigure; +_USBfreeze USBfreeze; +_USBtest USBtest; +_USBabout USBabout; + +// FW +_FWinit FWinit; +_FWopen FWopen; +_FWclose FWclose; +_FWshutdown FWshutdown; +_FWread32 FWread32; +_FWwrite32 FWwrite32; +_FWirqCallback FWirqCallback; + +_FWconfigure FWconfigure; +_FWfreeze FWfreeze; +_FWtest FWtest; +_FWabout FWabout; +#endif + +#endif /* __PS2EDEFS_H__ */ diff --git a/plugins/USBqemu/PS2Etypes.h b/plugins/USBqemu/PS2Etypes.h new file mode 100644 index 0000000000..194f6558f1 --- /dev/null +++ b/plugins/USBqemu/PS2Etypes.h @@ -0,0 +1,75 @@ +#ifndef __PS2ETYPES_H__ +#define __PS2ETYPES_H__ + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) +#endif + +#if defined (__linux__) // some distributions are lower case +#define __LINUX__ +#endif + +// Basic types +#if defined(_WIN32) + +typedef __int8 s8; +typedef __int16 s16; +typedef __int32 s32; +typedef __int64 s64; + +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; + +#if defined(__x86_64__) +typedef u64 uptr; +#else +typedef u32 uptr; +#endif + +#define PCSX2_ALIGNED16(x) __declspec(align(16)) x + +#else + +typedef char s8; +typedef short s16; +typedef int s32; +typedef long long s64; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +#if defined(__x86_64__) +typedef u64 uptr; +#else +typedef u32 uptr; +#endif + +#ifdef __LINUX__ +typedef union _LARGE_INTEGER +{ + long long QuadPart; +} LARGE_INTEGER; +#endif + +#if defined(__MINGW32__) +#define PCSX2_ALIGNED16(x) __declspec(align(16)) x +#else +#define PCSX2_ALIGNED16(x) x __attribute((aligned(16))) +#endif + +#ifndef __forceinline +#define __forceinline inline +#endif + +#endif + +typedef struct { + int size; + s8 *data; +} freezeData; + +#endif /* __PS2ETYPES_H__ */ diff --git a/plugins/USBqemu/ReadMe.txt b/plugins/USBqemu/ReadMe.txt new file mode 100644 index 0000000000..8a5e6cfe51 --- /dev/null +++ b/plugins/USBqemu/ReadMe.txt @@ -0,0 +1,26 @@ +USBlinuz v0.1 +------------- + + This is an extension to use with play station2 emulators + as PCSX2 (only one right now). + The plugin is free open source code. + +Usage: +----- + Place the file "usblinuz.dll" (win32) or usblinuz_64.dll (win64) + at the Plugin directory of the Emulator to use it. + +Changes: +------- +v0.1 (gigaherz) + * First Release + * Tested with Pcsx2 32 and 64bit + +Authors: +------- + + linuzappz + shadow + + + diff --git a/plugins/USBqemu/USB.cpp b/plugins/USBqemu/USB.cpp new file mode 100644 index 0000000000..b02285939d --- /dev/null +++ b/plugins/USBqemu/USB.cpp @@ -0,0 +1,252 @@ +/* USBlinuz + * Copyright (C) 2002-2004 USBlinuz Team + * + * 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 + */ + +#include +#include +#include + +#include "qemu-usb/vl.h" +#include "USB.h" + +const unsigned char version = PS2E_USB_VERSION; +const unsigned char revision = 0; +const unsigned char build = 1; // increase that with each version + +static char *libraryName = "Qemu USB Driver by Gigaherz" +#ifdef _DEBUG +" (debug)" +#endif +; + +OHCIState *qemu_ohci; + +Config conf; + +HWND gsWnd=NULL; + +u8 *usbR; +u8 *ram; +usbStruct usb; +USBcallback _USBirq; +FILE *usbLog; +int64_t usb_frame_time; +int64_t usb_bit_time; + +s64 clocks; + +u32 CALLBACK PS2EgetLibType() { + return PS2E_LT_USB; +} + +char* CALLBACK PS2EgetLibName() { + return libraryName; +} + +u32 CALLBACK PS2EgetLibVersion2(u32 type) { + return (version<<16) | (revision<<8) | build; +} + +void __Log(char *fmt, ...) { + va_list list; + + if (!conf.Log) return; + + va_start(list, fmt); + vfprintf(usbLog, fmt, list); + va_end(list); +} + +void USBirq(int cycles) +{ + USB_LOG("USBirq.\n"); + + _USBirq(cycles); +} + +s32 CALLBACK USBinit() { + LoadConfig(); + if (conf.Log) + { + usbLog = fopen("logs/usbLog.txt", "w"); + setvbuf(usbLog, NULL, _IONBF, 0); + USB_LOG("usblinuz plugin version %d,%d\n",revision,build); + USB_LOG("USBinit\n"); + } + + qemu_ohci = ohci_create(0x1f801600,2); + qemu_ohci->rhport[0].port.attach(&(qemu_ohci->rhport[0].port),usb_keyboard_init()); + + return 0; +} + +void CALLBACK USBshutdown() { + + qemu_ohci->rhport[0].port.dev->handle_destroy(qemu_ohci->rhport[0].port.dev); + + free(qemu_ohci); + +#ifdef _DEBUG + fclose(usbLog); +#endif +} + +s32 CALLBACK USBopen(void *pDsp) { + USB_LOG("USBopen\n"); + + HWND hWnd=(HWND)pDsp; + + if (!IsWindow (hWnd) && !IsBadReadPtr ((u32*)hWnd, 4)) + hWnd = *(HWND*)hWnd; + if (!IsWindow (hWnd)) + hWnd = NULL; + else + { + while (GetWindowLong (hWnd, GWL_STYLE) & WS_CHILD) + hWnd = GetParent (hWnd); + } + gsWnd = hWnd; + + return 0; +} + +void CALLBACK USBclose() { +} + +u8 CALLBACK USBread8(u32 addr) { + USB_LOG("* Invalid 8bit read at address %lx\n", addr); + return 0; +} + +u16 CALLBACK USBread16(u32 addr) { + USB_LOG("* Invalid 16bit read at address %lx\n", addr); + return 0; +} + +u32 CALLBACK USBread32(u32 addr) { + u32 hard; + + hard=ohci_mem_read(qemu_ohci,addr); + + USB_LOG("* Known 32bit read at address %lx: %lx\n", addr, hard); + + return hard; +} + +void CALLBACK USBwrite8(u32 addr, u8 value) { + USB_LOG("* Invalid 8bit write at address %lx value %x\n", addr, value); +} + +void CALLBACK USBwrite16(u32 addr, u16 value) { + USB_LOG("* Invalid 16bit write at address %lx value %x\n", addr, value); +} + +void CALLBACK USBwrite32(u32 addr, u32 value) { + USB_LOG("* Known 32bit write at address %lx value %lx\n", addr, value); + ohci_mem_write(qemu_ohci,addr,value); +} + +void CALLBACK USBirqCallback(USBcallback callback) { + _USBirq = callback; +} + +extern u32 bits; + +int CALLBACK _USBirqHandler(void) +{ + //fprintf(stderr," * USB: IRQ Acknowledged.\n"); + //qemu_ohci->intr_status&=~bits; + return 1; +} + +USBhandler CALLBACK USBirqHandler(void) { + return (USBhandler)_USBirqHandler; +} + +void CALLBACK USBsetRAM(void *mem) { + ram = (u8*)mem; +} + +// extended funcs + +char USBfreezeID[] = "USB STv0"; + +typedef struct { + OHCIState t; +} USBfreezeData; + +s32 CALLBACK USBfreeze(int mode, freezeData *data) { + USBfreezeData *usbd; + + if (mode == FREEZE_LOAD) { + usbd = (USBfreezeData*)data->data; + if (data->size != sizeof(USBfreezeData)) return -1; + + memcpy(qemu_ohci, usbd, sizeof(OHCIState)); + } else + if (mode == FREEZE_SAVE) { + data->size = sizeof(USBfreezeData); + data->data = (s8*)malloc(data->size); + if (data->data == NULL) return -1; + usbd = (USBfreezeData*)data->data; + memcpy(usbd, qemu_ohci, sizeof(OHCIState)); + } + + return 0; +} + +void CALLBACK USBasync(u32 cycles) +{ + clocks+=cycles; + if(qemu_ohci->eof_timer>0) + { + while(cycles>=qemu_ohci->eof_timer) + { + cycles-=qemu_ohci->eof_timer; + qemu_ohci->eof_timer=0; + ohci_frame_boundary(qemu_ohci); + } + if((cycles>0)&&(qemu_ohci->eof_timer>0)) + qemu_ohci->eof_timer-=cycles; + } +} + +s32 CALLBACK USBtest() { + return 0; +} + +void cpu_physical_memory_rw(u32 addr, u8 *buf, + int len, int is_write) +{ + if(is_write) + memcpy(&(ram[addr]),buf,len); + else + memcpy(buf,&(ram[addr]),len); +} + +s64 get_clock() +{ + return clocks; +} + +void *qemu_mallocz(uint32_t size) +{ + void *m=malloc(size); + if(!m) return NULL; + memset(m,0,size); + return m; +} diff --git a/plugins/USBqemu/USB.h b/plugins/USBqemu/USB.h new file mode 100644 index 0000000000..a83f6f3e08 --- /dev/null +++ b/plugins/USBqemu/USB.h @@ -0,0 +1,313 @@ +/* USBlinuz + * Copyright (C) 2002-2004 USBlinuz Team + * + * 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 + */ + +#pragma once + +#ifndef __USB_H__ +#define __USB_H__ + +#include + +#define USBdefs +#include "PS2Edefs.h" + +#ifdef __WIN32__ + +#define usleep(x) Sleep(x / 1000) +#include +#include + +#else + +#include + +#define __inline inline + +#endif + +#define USB_LOG __Log + +typedef struct { + int Log; +} Config; + +extern Config conf; + +extern u8 *usbR; +extern u8 *ram; + +typedef struct { + int unused; +} usbStruct; + +extern usbStruct usb; + +#define usbRs8(mem) usbR[(mem) & 0xffff] +#define usbRs16(mem) (*(s16*)&usbR[(mem) & 0xffff]) +#define usbRs32(mem) (*(s32*)&usbR[(mem) & 0xffff]) +#define usbRu8(mem) (*(u8*) &usbR[(mem) & 0xffff]) +#define usbRu16(mem) (*(u16*)&usbR[(mem) & 0xffff]) +#define usbRu32(mem) (*(u32*)&usbR[(mem) & 0xffff]) + +#define PSXCLK 36864000 /* 36.864 Mhz */ + +extern USBcallback _USBirq; +void USBirq(int); + +void SaveConfig(); +void LoadConfig(); + +extern FILE *usbLog; +void __Log(char *fmt, ...); + +void SysMessage(char *fmt, ...); + +#include "qemu-usb/vl.h" + +#define DEBUG_OHCI +/* Dump packet contents. */ +//#define DEBUG_PACKET +/* This causes frames to occur 1000x slower */ +//#define OHCI_TIME_WARP 1 + +/* Number of Downstream Ports on the root hub. */ + +#define OHCI_MAX_PORTS 15 + +extern int64_t usb_frame_time; +extern int64_t usb_bit_time; + +typedef struct OHCIPort { + USBPort port; + uint32_t ctrl; +} OHCIPort; + +typedef uint32_t target_phys_addr_t; + +typedef struct { + target_phys_addr_t mem_base; + int mem; + int num_ports; + + uint64_t eof_timer; + int64_t sof_time; + + /* OHCI state */ + /* Control partition */ + uint32_t ctl, status; + uint32_t intr_status; + uint32_t intr; + + /* memory pointer partition */ + uint32_t hcca; + uint32_t ctrl_head, ctrl_cur; + uint32_t bulk_head, bulk_cur; + uint32_t per_cur; + uint32_t done; + int done_count; + + /* Frame counter partition */ + uint32_t fsmps:15; + uint32_t fit:1; + uint32_t fi:14; + uint32_t frt:1; + uint16_t frame_number; + uint16_t padding; + uint32_t pstart; + uint32_t lst; + + /* Root Hub partition */ + uint32_t rhdesc_a, rhdesc_b; + uint32_t rhstatus; + OHCIPort rhport[OHCI_MAX_PORTS]; +} OHCIState; + +/* Host Controller Communications Area */ +struct ohci_hcca { + uint32_t intr[32]; + uint16_t frame, pad; + uint32_t done; +}; + +/* Bitfields for the first word of an Endpoint Desciptor. */ +#define OHCI_ED_FA_SHIFT 0 +#define OHCI_ED_FA_MASK (0x7f<> OHCI_##field##_SHIFT) + +#define OHCI_SET_BM(val, field, newval) do { \ + val &= ~OHCI_##field##_MASK; \ + val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \ + } while(0) + +/* endpoint descriptor */ +struct ohci_ed { + uint32_t flags; + uint32_t tail; + uint32_t head; + uint32_t next; +}; + +/* General transfer descriptor */ +struct ohci_td { + uint32_t flags; + uint32_t cbp; + uint32_t next; + uint32_t be; +}; + +#define USB_HZ 12000000 + +/* OHCI Local stuff */ +#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) +#define OHCI_CTL_PLE (1<<2) +#define OHCI_CTL_IE (1<<3) +#define OHCI_CTL_CLE (1<<4) +#define OHCI_CTL_BLE (1<<5) +#define OHCI_CTL_HCFS ((1<<6)|(1<<7)) +#define OHCI_USB_RESET 0x00 +#define OHCI_USB_RESUME 0x40 +#define OHCI_USB_OPERATIONAL 0x80 +#define OHCI_USB_SUSPEND 0xc0 +#define OHCI_CTL_IR (1<<8) +#define OHCI_CTL_RWC (1<<9) +#define OHCI_CTL_RWE (1<<10) + +#define OHCI_STATUS_HCR (1<<0) +#define OHCI_STATUS_CLF (1<<1) +#define OHCI_STATUS_BLF (1<<2) +#define OHCI_STATUS_OCR (1<<3) +#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) + +#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */ +#define OHCI_INTR_WD (1<<1) /* HcDoneHead writeback */ +#define OHCI_INTR_SF (1<<2) /* Start of frame */ +#define OHCI_INTR_RD (1<<3) /* Resume detect */ +#define OHCI_INTR_UE (1<<4) /* Unrecoverable error */ +#define OHCI_INTR_FNO (1<<5) /* Frame number overflow */ +#define OHCI_INTR_RHSC (1<<6) /* Root hub status change */ +#define OHCI_INTR_OC (1<<30) /* Ownership change */ +#define OHCI_INTR_MIE (1<<31) /* Master Interrupt Enable */ + +#define OHCI_HCCA_SIZE 0x100 +#define OHCI_HCCA_MASK 0xffffff00 + +#define OHCI_EDPTR_MASK 0xfffffff0 + +#define OHCI_FMI_FI 0x00003fff +#define OHCI_FMI_FSMPS 0xffff0000 +#define OHCI_FMI_FIT 0x80000000 + +#define OHCI_FR_RT (1<<31) + +#define OHCI_LS_THRESH 0x628 + +#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */ +#define OHCI_RHA_PSM (1<<8) +#define OHCI_RHA_NPS (1<<9) +#define OHCI_RHA_DT (1<<10) +#define OHCI_RHA_OCPM (1<<11) +#define OHCI_RHA_NOCP (1<<12) +#define OHCI_RHA_POTPGT_MASK 0xff000000 + +#define OHCI_RHS_LPS (1<<0) +#define OHCI_RHS_OCI (1<<1) +#define OHCI_RHS_DRWE (1<<15) +#define OHCI_RHS_LPSC (1<<16) +#define OHCI_RHS_OCIC (1<<17) +#define OHCI_RHS_CRWE (1<<31) + +#define OHCI_PORT_CCS (1<<0) +#define OHCI_PORT_PES (1<<1) +#define OHCI_PORT_PSS (1<<2) +#define OHCI_PORT_POCI (1<<3) +#define OHCI_PORT_PRS (1<<4) +#define OHCI_PORT_PPS (1<<8) +#define OHCI_PORT_LSDA (1<<9) +#define OHCI_PORT_CSC (1<<16) +#define OHCI_PORT_PESC (1<<17) +#define OHCI_PORT_PSSC (1<<18) +#define OHCI_PORT_OCIC (1<<19) +#define OHCI_PORT_PRSC (1<<20) +#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \ + |OHCI_PORT_OCIC|OHCI_PORT_PRSC) + +#define OHCI_TD_DIR_SETUP 0x0 +#define OHCI_TD_DIR_OUT 0x1 +#define OHCI_TD_DIR_IN 0x2 +#define OHCI_TD_DIR_RESERVED 0x3 + +#define OHCI_CC_NOERROR 0x0 +#define OHCI_CC_CRC 0x1 +#define OHCI_CC_BITSTUFFING 0x2 +#define OHCI_CC_DATATOGGLEMISMATCH 0x3 +#define OHCI_CC_STALL 0x4 +#define OHCI_CC_DEVICENOTRESPONDING 0x5 +#define OHCI_CC_PIDCHECKFAILURE 0x6 +#define OHCI_CC_UNDEXPETEDPID 0x7 +#define OHCI_CC_DATAOVERRUN 0x8 +#define OHCI_CC_DATAUNDERRUN 0x9 +#define OHCI_CC_BUFFEROVERRUN 0xc +#define OHCI_CC_BUFFERUNDERRUN 0xd + +int64_t get_clock(); + +OHCIState *ohci_create(uint32_t base, int ports); + +uint32_t ohci_mem_read(OHCIState *ohci, uint32_t addr ); +void ohci_mem_write(OHCIState *ohci, uint32_t addr, uint32_t value ); +void ohci_frame_boundary(void *opaque); + +int ohci_bus_start(OHCIState *ohci); +void ohci_bus_stop(OHCIState *ohci); + +USBDevice *usb_hub_init(int nb_ports); +USBDevice *usb_msd_init(const char *filename); +USBDevice *eyetoy_init(void); +USBDevice *usb_mouse_init(void); + +#endif diff --git a/plugins/USBqemu/Win32/Config.cpp b/plugins/USBqemu/Win32/Config.cpp new file mode 100644 index 0000000000..c46552a4a6 --- /dev/null +++ b/plugins/USBqemu/Win32/Config.cpp @@ -0,0 +1,51 @@ +#include + +#include "USB.h" + +extern HINSTANCE hInst; +void SaveConfig() +{ + + Config *Conf1 = &conf; + char *szTemp; + char szIniFile[256], szValue[256]; + + GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); + szTemp = strrchr(szIniFile, '\\'); + + if(!szTemp) return; + strcpy(szTemp, "\\inis\\usblinuz.ini"); + sprintf(szValue,"%u",Conf1->Log); + WritePrivateProfileString("Interface", "Logging",szValue,szIniFile); + +} + +void LoadConfig() { + FILE *fp; + + + Config *Conf1 = &conf; + char *szTemp; + char szIniFile[256], szValue[256]; + + GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); + szTemp = strrchr(szIniFile, '\\'); + + if(!szTemp) return ; + strcpy(szTemp, "\\inis\\usblinuz.ini"); + fp=fopen("inis\\usblinuz.ini","rt");//check if usbnull.ini really exists + if (!fp) + { + CreateDirectory("inis",NULL); + memset(&conf, 0, sizeof(conf)); + conf.Log = 0;//default value + SaveConfig();//save and return + return ; + } + fclose(fp); + GetPrivateProfileString("Interface", "Logging", NULL, szValue, 20, szIniFile); + Conf1->Log = strtoul(szValue, NULL, 10); + return ; + +} + diff --git a/plugins/USBqemu/Win32/USBlinuz.aps b/plugins/USBqemu/Win32/USBlinuz.aps new file mode 100644 index 0000000000..c56328c8c9 Binary files /dev/null and b/plugins/USBqemu/Win32/USBlinuz.aps differ diff --git a/plugins/USBqemu/Win32/USBlinuz.def b/plugins/USBqemu/Win32/USBlinuz.def new file mode 100644 index 0000000000..187d81a501 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz.def @@ -0,0 +1,28 @@ +; USBlinuz.def : Declares the module parameters for the DLL. + +LIBRARY "USBqemu" +DESCRIPTION 'USBqemu Driver' + +EXPORTS + ; Explicit exports can go here + PS2EgetLibType @2 + PS2EgetLibName @3 + PS2EgetLibVersion2 @4 + USBinit @5 + USBshutdown @6 + USBopen @7 + USBclose @8 + USBread8 @9 + USBread16 @10 + USBread32 @11 + USBwrite8 @12 + USBwrite16 @13 + USBwrite32 @14 + USBirqCallback @15 + USBirqHandler @16 + USBsetRAM @17 + USBasync @18 + + USBconfigure @19 + USBtest @20 + USBabout @21 diff --git a/plugins/USBqemu/Win32/USBlinuz.rc b/plugins/USBqemu/Win32/USBlinuz.rc new file mode 100644 index 0000000000..ac4e99e066 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz.rc @@ -0,0 +1,156 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Spanish (Argentina) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESS) +#ifdef _WIN32 +LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 212, 121 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "USBconfigure" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,48,100,50,14 + PUSHBUTTON "Cancel",IDCANCEL,113,100,50,14 + CONTROL "Enable Logging (for develop use only)",IDC_LOGGING, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,144,15 + COMBOBOX IDC_COMBO1,7,38,138,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Device in Port 1:",IDC_STATIC,8,30,54,8 + COMBOBOX IDC_COMBO2,7,63,138,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Device in Port 2:",IDC_STATIC,8,55,54,8 +END + +IDD_ABOUT DIALOGEX 0, 0, 177, 106 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "USBabout" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,65,85,50,14 + LTEXT "USBqemu Driver",IDC_NAME,70,10,54,8 + GROUPBOX "",IDC_STATIC,5,35,170,40 + LTEXT "Author: gigaherz ",IDC_STATIC,20,20,141,10 + LTEXT "Parts of this code were originally from the QEMU project.",IDC_STATIC,14,44,149,26 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 205 + TOPMARGIN, 7 + BOTTOMMARGIN, 114 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 170 + TOPMARGIN, 7 + BOTTOMMARGIN, 99 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_CONFIG DLGINIT +BEGIN + IDC_COMBO1, 0x403, 10, 0 +0x6f4e, 0x4420, 0x7665, 0x6369, 0x0065, + IDC_COMBO1, 0x403, 9, 0 +0x654b, 0x6279, 0x616f, 0x6472, "\000" + IDC_COMBO1, 0x403, 6, 0 +0x6f4d, 0x7375, 0x0065, + IDC_COMBO1, 0x403, 14, 0 +0x7945, 0x7465, 0x796f, 0x4320, 0x6d61, 0x7265, 0x0061, + IDC_COMBO1, 0x403, 20, 0 +0x6953, 0x676e, 0x7473, 0x7261, 0x4d20, 0x6369, 0x6f72, 0x6870, 0x6e6f, +0x0065, + IDC_COMBO2, 0x403, 10, 0 +0x6f4e, 0x4420, 0x7665, 0x6369, 0x0065, + IDC_COMBO2, 0x403, 9, 0 +0x654b, 0x6279, 0x616f, 0x6472, "\000" + IDC_COMBO2, 0x403, 6, 0 +0x6f4d, 0x7375, 0x0065, + IDC_COMBO2, 0x403, 14, 0 +0x7945, 0x7465, 0x796f, 0x4320, 0x6d61, 0x7265, 0x0061, + IDC_COMBO2, 0x403, 20, 0 +0x6953, 0x676e, 0x7473, 0x7261, 0x4d20, 0x6369, 0x6f72, 0x6870, 0x6e6f, +0x0065, + 0 +END + +#endif // Spanish (Argentina) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/USBqemu/Win32/USBlinuz_vc2005.sln b/plugins/USBqemu/Win32/USBlinuz_vc2005.sln new file mode 100644 index 0000000000..9fb2025f86 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz_vc2005.sln @@ -0,0 +1,25 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "USBlinuz", "USBlinuz_vc2005.vcproj", "{BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|Win32.Build.0 = Debug|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|x64.ActiveCfg = Debug|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|x64.Build.0 = Debug|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|Win32.ActiveCfg = Release|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|Win32.Build.0 = Release|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|x64.ActiveCfg = Release|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/USBqemu/Win32/USBlinuz_vc2005.vcproj b/plugins/USBqemu/Win32/USBlinuz_vc2005.vcproj new file mode 100644 index 0000000000..c89a2f65c0 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz_vc2005.vcproj @@ -0,0 +1,669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/USBqemu/Win32/USBlinuz_vc2005_x64.sln b/plugins/USBqemu/Win32/USBlinuz_vc2005_x64.sln new file mode 100644 index 0000000000..41cf887a29 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz_vc2005_x64.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "USBlinuz", "USBlinuz_vc2005_x64.vcproj", "{BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|Win32.Build.0 = Debug|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|x64.ActiveCfg = Debug|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|x64.Build.0 = Debug|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|Win32.ActiveCfg = Release|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|Win32.Build.0 = Release|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|x64.ActiveCfg = Release|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/USBqemu/Win32/USBlinuz_vc2005_x64.vcproj b/plugins/USBqemu/Win32/USBlinuz_vc2005_x64.vcproj new file mode 100644 index 0000000000..c89a2f65c0 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz_vc2005_x64.vcproj @@ -0,0 +1,669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/USBqemu/Win32/USBlinuz_vc2008b2.sln b/plugins/USBqemu/Win32/USBlinuz_vc2008b2.sln new file mode 100644 index 0000000000..e13082a8b5 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz_vc2008b2.sln @@ -0,0 +1,25 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "USBlinuz", "USBlinuz_vc2008b2.vcproj", "{BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|Win32.Build.0 = Debug|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|x64.ActiveCfg = Debug|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Debug|x64.Build.0 = Debug|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|Win32.ActiveCfg = Release|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|Win32.Build.0 = Release|Win32 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|x64.ActiveCfg = Release|x64 + {BF7B81A5-E348-4F7C-A69F-F74C8EEEAD70}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/USBqemu/Win32/USBlinuz_vc2008b2.vcproj b/plugins/USBqemu/Win32/USBlinuz_vc2008b2.vcproj new file mode 100644 index 0000000000..09bd52e717 --- /dev/null +++ b/plugins/USBqemu/Win32/USBlinuz_vc2008b2.vcproj @@ -0,0 +1,669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/USBqemu/Win32/Win32.cpp b/plugins/USBqemu/Win32/Win32.cpp new file mode 100644 index 0000000000..7647d9702b --- /dev/null +++ b/plugins/USBqemu/Win32/Win32.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +#include "USB.h" +#include "resource.h" + +HINSTANCE hInst; + +void SysMessage(char *fmt, ...) { + va_list list; + char tmp[512]; + + va_start(list,fmt); + vsprintf(tmp,fmt,list); + va_end(list); + MessageBox(0, tmp, "USBlinuz Msg", 0); +} + +BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + LoadConfig(); + if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDCANCEL: + EndDialog(hW, TRUE); + return TRUE; + case IDOK: + if (IsDlgButtonChecked(hW, IDC_LOGGING)) + conf.Log = 1; + else conf.Log = 0; + SaveConfig(); + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + + +BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDOK: + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +void CALLBACK USBconfigure() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_CONFIG), + GetActiveWindow(), + (DLGPROC)ConfigureDlgProc); +} + +void CALLBACK USBabout() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_ABOUT), + GetActiveWindow(), + (DLGPROC)AboutDlgProc); +} + +BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT + DWORD dwReason, + LPVOID lpReserved) { + hInst = (HINSTANCE)hModule; + return TRUE; // very quick :) +} diff --git a/plugins/USBqemu/Win32/resource.h b/plugins/USBqemu/Win32/resource.h new file mode 100644 index 0000000000..ad8f21c11d --- /dev/null +++ b/plugins/USBqemu/Win32/resource.h @@ -0,0 +1,23 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by USBlinuz.rc +// +#define IDD_CONFDLG 101 +#define IDD_CONFIG 101 +#define IDD_ABOUT 103 +#define IDC_NAME 1000 +#define IDC_CHECK1 1007 +#define IDC_LOGGING 1007 +#define IDC_COMBO1 1008 +#define IDC_COMBO2 1009 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1009 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/USBqemu/qemu-usb/usb-base.cpp b/plugins/USBqemu/qemu-usb/usb-base.cpp new file mode 100644 index 0000000000..34aac5fa9b --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb-base.cpp @@ -0,0 +1,193 @@ +/* + * QEMU USB emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +void usb_attach(USBPort *port, USBDevice *dev) +{ + port->attach(port, dev); +} + +/**********************/ +/* generic USB device helpers (you are not forced to use them when + writing your USB device driver, but they help handling the + protocol) +*/ + +#define SETUP_STATE_IDLE 0 +#define SETUP_STATE_DATA 1 +#define SETUP_STATE_ACK 2 + +int usb_generic_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + int l, ret = 0; + + switch(pid) { + case USB_MSG_ATTACH: + s->state = USB_STATE_ATTACHED; + break; + case USB_MSG_DETACH: + s->state = USB_STATE_NOTATTACHED; + break; + case USB_MSG_RESET: + s->remote_wakeup = 0; + s->addr = 0; + s->state = USB_STATE_DEFAULT; + s->handle_reset(s); + break; + case USB_TOKEN_SETUP: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + if (len != 8) + goto fail; + memcpy(s->setup_buf, data, 8); + s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + if (s->setup_buf[0] & USB_DIR_IN) { + ret = s->handle_control(s, + (s->setup_buf[0] << 8) | s->setup_buf[1], + (s->setup_buf[3] << 8) | s->setup_buf[2], + (s->setup_buf[5] << 8) | s->setup_buf[4], + s->setup_len, + s->data_buf); + if (ret < 0) + return ret; + if (ret < s->setup_len) + s->setup_len = ret; + s->setup_state = SETUP_STATE_DATA; + } else { + if (s->setup_len == 0) + s->setup_state = SETUP_STATE_ACK; + else + s->setup_state = SETUP_STATE_DATA; + } + break; + case USB_TOKEN_IN: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + switch(devep) { + case 0: + switch(s->setup_state) { + case SETUP_STATE_ACK: + if (!(s->setup_buf[0] & USB_DIR_IN)) { + s->setup_state = SETUP_STATE_IDLE; + ret = s->handle_control(s, + (s->setup_buf[0] << 8) | s->setup_buf[1], + (s->setup_buf[3] << 8) | s->setup_buf[2], + (s->setup_buf[5] << 8) | s->setup_buf[4], + s->setup_len, + s->data_buf); + if (ret > 0) + ret = 0; + } else { + /* return 0 byte */ + } + break; + case SETUP_STATE_DATA: + if (s->setup_buf[0] & USB_DIR_IN) { + l = s->setup_len - s->setup_index; + if (l > len) + l = len; + memcpy(data, s->data_buf + s->setup_index, l); + s->setup_index += l; + if (s->setup_index >= s->setup_len) + s->setup_state = SETUP_STATE_ACK; + ret = l; + } else { + s->setup_state = SETUP_STATE_IDLE; + goto fail; + } + break; + default: + goto fail; + } + break; + default: + ret = s->handle_data(s, pid, devep, data, len); + break; + } + break; + case USB_TOKEN_OUT: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + switch(devep) { + case 0: + switch(s->setup_state) { + case SETUP_STATE_ACK: + if (s->setup_buf[0] & USB_DIR_IN) { + s->setup_state = SETUP_STATE_IDLE; + /* transfer OK */ + } else { + /* ignore additionnal output */ + } + break; + case SETUP_STATE_DATA: + if (!(s->setup_buf[0] & USB_DIR_IN)) { + l = s->setup_len - s->setup_index; + if (l > len) + l = len; + memcpy(s->data_buf + s->setup_index, data, l); + s->setup_index += l; + if (s->setup_index >= s->setup_len) + s->setup_state = SETUP_STATE_ACK; + ret = l; + } else { + s->setup_state = SETUP_STATE_IDLE; + goto fail; + } + break; + default: + goto fail; + } + break; + default: + ret = s->handle_data(s, pid, devep, data, len); + break; + } + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +/* XXX: fix overflow */ +int set_usb_string(uint8_t *buf, const char *str) +{ + int len, i; + uint8_t *q; + + q = buf; + len = strlen(str); + *q++ = 2 * len + 2; + *q++ = 3; + for(i = 0; i < len; i++) { + *q++ = str[i]; + *q++ = 0; + } + return q - buf; +} diff --git a/plugins/USBqemu/qemu-usb/usb-hid.cpp b/plugins/USBqemu/qemu-usb/usb-hid.cpp new file mode 100644 index 0000000000..04b78fc0f0 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb-hid.cpp @@ -0,0 +1,551 @@ +/* + * QEMU USB HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +#define USB_MOUSE 1 +#define USB_TABLET 2 + +typedef struct USBMouseState { + USBDevice dev; + int dx, dy, dz, buttons_state; + int x, y; + int kind; + int mouse_grabbed; +} USBMouseState; + +/* mostly the same values as the Bochs USB Mouse device */ +static const uint8_t qemu_mouse_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x00, /* u16 bcdUSB; v1.0 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x27, 0x06, /* u16 idVendor; */ + 0x01, 0x00, /* u16 idProduct; */ + 0x00, 0x00, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_mouse_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 50, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_mouse_hid_report_descriptor[] = { + 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, + 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, + 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, + 0xC0, 0xC0, +}; + +static const uint8_t qemu_tablet_hid_report_descriptor[] = { + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x01, /* Usage Mouse */ + 0xA1, 0x01, /* Collection Application */ + 0x09, 0x01, /* Usage Pointer */ + 0xA1, 0x00, /* Collection Physical */ + 0x05, 0x09, /* Usage Page Button */ + 0x19, 0x01, /* Usage Minimum Button 1 */ + 0x29, 0x03, /* Usage Maximum Button 3 */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x25, 0x01, /* Logical Maximum 1 */ + 0x95, 0x03, /* Report Count 3 */ + 0x75, 0x01, /* Report Size 1 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x01, /* Report Count 1 */ + 0x75, 0x05, /* Report Size 5 */ + 0x81, 0x01, /* Input (Cnst, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x30, /* Usage X */ + 0x09, 0x31, /* Usage Y */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */ + 0x35, 0x00, /* Physical Minimum 0 */ + 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */ + 0x75, 0x10, /* Report Size 16 */ + 0x95, 0x02, /* Report Count 2 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x38, /* Usage Wheel */ + 0x15, 0x81, /* Logical Minimum -127 */ + 0x25, 0x7F, /* Logical Maximum 127 */ + 0x35, 0x00, /* Physical Minimum 0 (same as logical) */ + 0x45, 0x00, /* Physical Maximum 0 (same as logical) */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x02, /* Input (Data, Var, Rel) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +static void usb_mouse_event(void *opaque, + int dx1, int dy1, int dz1, int buttons_state) +{ + USBMouseState *s = opaque; + + s->dx += dx1; + s->dy += dy1; + s->dz += dz1; + s->buttons_state = buttons_state; +} + +static void usb_tablet_event(void *opaque, + int x, int y, int dz, int buttons_state) +{ + USBMouseState *s = opaque; + + s->x = x; + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; +} + +static inline int int_clamp(int val, int vmin, int vmax) +{ + if (val < vmin) + return vmin; + else if (val > vmax) + return vmax; + else + return val; +} + +static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_mouse_event, s, 0); + s->mouse_grabbed = 1; + } + + dx = int_clamp(s->dx, -128, 127); + dy = int_clamp(s->dy, -128, 127); + dz = int_clamp(s->dz, -128, 127); + + s->dx -= dx; + s->dy -= dy; + s->dz -= dz; + + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = dx; + buf[2] = dy; + l = 3; + if (len >= 4) { + buf[3] = dz; + l = 4; + } + return l; +} + +static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_tablet_event, s, 1); + s->mouse_grabbed = 1; + } + + dz = int_clamp(s->dz, -128, 127); + s->dz -= dz; + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = s->x & 0xff; + buf[2] = s->x >> 8; + buf[3] = s->y & 0xff; + buf[4] = s->y >> 8; + buf[5] = dz; + l = 6; + + return l; +} + +static void usb_mouse_handle_reset(USBDevice *dev) +{ + USBMouseState *s = (USBMouseState *)dev; + + s->dx = 0; + s->dy = 0; + s->dz = 0; + s->x = 0; + s->y = 0; + s->buttons_state = 0; +} + +static int usb_mouse_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_mouse_dev_descriptor, + sizeof(qemu_mouse_dev_descriptor)); + ret = sizeof(qemu_mouse_dev_descriptor); + break; + case USB_DT_CONFIG: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_config_descriptor, + sizeof(qemu_mouse_config_descriptor)); + ret = sizeof(qemu_mouse_config_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_config_descriptor, + sizeof(qemu_tablet_config_descriptor)); + ret = sizeof(qemu_tablet_config_descriptor); + } + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "1"); + break; + case 2: + /* product description */ + if (s->kind == USB_MOUSE) + ret = set_usb_string(data, "QEMU USB Mouse"); + else if (s->kind == USB_TABLET) + ret = set_usb_string(data, "QEMU USB Tablet"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "PCSX2/QEMU"); + break; + case 4: + ret = set_usb_string(data, "HID Mouse"); + break; + case 5: + ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case 0x22: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); + ret = sizeof(qemu_mouse_hid_report_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, + sizeof(qemu_tablet_hid_report_descriptor)); + ret = sizeof(qemu_tablet_hid_report_descriptor); + } + break; + default: + goto fail; + } + break; + case GET_REPORT: + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, length); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, length); + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_mouse_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static void usb_mouse_handle_destroy(USBDevice *dev) +{ + USBMouseState *s = (USBMouseState *)dev; + + qemu_add_mouse_event_handler(NULL, NULL, 0); + free(s); +} + +USBDevice *usb_tablet_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->dev.handle_destroy = usb_mouse_handle_destroy; + s->kind = USB_TABLET; + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); + + return (USBDevice *)s; +} + +USBDevice *usb_mouse_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->dev.handle_destroy = usb_mouse_handle_destroy; + s->kind = USB_MOUSE; + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); + + return (USBDevice *)s; +} diff --git a/plugins/USBqemu/qemu-usb/usb-hub.cpp b/plugins/USBqemu/qemu-usb/usb-hub.cpp new file mode 100644 index 0000000000..4654863870 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb-hub.cpp @@ -0,0 +1,565 @@ +/* + * QEMU USB HUB emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG + +#define MAX_PORTS 8 + +typedef struct USBHubPort { + USBPort port; + uint16_t wPortStatus; + uint16_t wPortChange; +} USBHubPort; + +typedef struct USBHubState { + USBDevice dev; + int nb_ports; + USBHubPort ports[MAX_PORTS]; +} USBHubState; + +#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) +#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) +#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) +#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) + +#define PORT_STAT_CONNECTION 0x0001 +#define PORT_STAT_ENABLE 0x0002 +#define PORT_STAT_SUSPEND 0x0004 +#define PORT_STAT_OVERCURRENT 0x0008 +#define PORT_STAT_RESET 0x0010 +#define PORT_STAT_POWER 0x0100 +#define PORT_STAT_LOW_SPEED 0x0200 +#define PORT_STAT_HIGH_SPEED 0x0400 +#define PORT_STAT_TEST 0x0800 +#define PORT_STAT_INDICATOR 0x1000 + +#define PORT_STAT_C_CONNECTION 0x0001 +#define PORT_STAT_C_ENABLE 0x0002 +#define PORT_STAT_C_SUSPEND 0x0004 +#define PORT_STAT_C_OVERCURRENT 0x0008 +#define PORT_STAT_C_RESET 0x0010 + +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVERCURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOWSPEED 9 +#define PORT_HIGHSPEED 10 +#define PORT_C_CONNECTION 16 +#define PORT_C_ENABLE 17 +#define PORT_C_SUSPEND 18 +#define PORT_C_OVERCURRENT 19 +#define PORT_C_RESET 20 +#define PORT_TEST 21 +#define PORT_INDICATOR 22 + +/* same as Linux kernel root hubs */ + +static const uint8_t qemu_hub_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x01, /* u16 bcdUSB; v1.1 */ + + 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x00, 0x00, /* u16 idVendor; */ + 0x00, 0x00, /* u16 idProduct; */ + 0x01, 0x01, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +/* XXX: patch interrupt size */ +static const uint8_t qemu_hub_config_descriptor[] = { + + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xc0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* u8 if_bInterfaceSubClass; */ + 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_hub_hub_descriptor[] = +{ + 0x00, /* u8 bLength; patched in later */ + 0x29, /* u8 bDescriptorType; Hub-descriptor */ + 0x00, /* u8 bNbrPorts; (patched later) */ + 0x0a, /* u16 wHubCharacteristics; */ + 0x00, /* (per-port OC, no power switching) */ + 0x01, /* u8 bPwrOn2pwrGood; 2ms */ + 0x00 /* u8 bHubContrCurrent; 0 mA */ + + /* DeviceRemovable and PortPwrCtrlMask patched in later */ +}; + +static void usb_hub_attach(USBPort *port1, USBDevice *dev) +{ + USBHubState *s =(USBHubState *) port1->opaque; + USBHubPort *port = (USBHubPort *)&s->ports[port1->index]; + + if (dev) { + if (port->port.dev) + usb_attach(port1, NULL); + + port->wPortStatus |= PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (dev->speed == USB_SPEED_LOW) + port->wPortStatus |= PORT_STAT_LOW_SPEED; + else + port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + port->port.dev = dev; + /* send the attach message */ + dev->handle_packet(dev, + USB_MSG_ATTACH, 0, 0, NULL, 0); + } else { + dev = port->port.dev; + if (dev) { + port->wPortStatus &= ~PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->wPortStatus & PORT_STAT_ENABLE) { + port->wPortStatus &= ~PORT_STAT_ENABLE; + port->wPortChange |= PORT_STAT_C_ENABLE; + } + /* send the detach message */ + dev->handle_packet(dev, + USB_MSG_DETACH, 0, 0, NULL, 0); + port->port.dev = NULL; + } + } +} + +static void usb_hub_handle_reset(USBDevice *dev) +{ + /* XXX: do it */ +} + +static int usb_hub_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBHubState *s = (USBHubState *)dev; + int ret; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == 0 && index != 0x81) { /* clear ep halt */ + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_hub_dev_descriptor, + sizeof(qemu_hub_dev_descriptor)); + ret = sizeof(qemu_hub_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, qemu_hub_config_descriptor, + sizeof(qemu_hub_config_descriptor)); + + /* status change endpoint size based on number + * of ports */ + data[22] = (s->nb_ports + 1 + 7) / 8; + + ret = sizeof(qemu_hub_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "314159"); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "QEMU USB Hub"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "PCSX2/QEMU"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* usb specific requests */ + case GetHubStatus: + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0; + ret = 4; + break; + case GetPortStatus: + { + unsigned int n = index - 1; + USBHubPort *port; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + data[0] = port->wPortStatus; + data[1] = port->wPortStatus >> 8; + data[2] = port->wPortChange; + data[3] = port->wPortChange >> 8; + ret = 4; + } + break; + case SetHubFeature: + case ClearHubFeature: + if (value == 0 || value == 1) { + } else { + goto fail; + } + ret = 0; + break; + case SetPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + USBDevice *dev; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + dev = port->port.dev; + switch(value) { + case PORT_SUSPEND: + port->wPortStatus |= PORT_STAT_SUSPEND; + break; + case PORT_RESET: + if (dev) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + port->wPortChange |= PORT_STAT_C_RESET; + /* set enable bit */ + port->wPortStatus |= PORT_STAT_ENABLE; + } + break; + case PORT_POWER: + break; + default: + goto fail; + } + ret = 0; + } + break; + case ClearPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + USBDevice *dev; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + dev = port->port.dev; + switch(value) { + case PORT_ENABLE: + port->wPortStatus &= ~PORT_STAT_ENABLE; + break; + case PORT_C_ENABLE: + port->wPortChange &= ~PORT_STAT_C_ENABLE; + break; + case PORT_SUSPEND: + port->wPortStatus &= ~PORT_STAT_SUSPEND; + break; + case PORT_C_SUSPEND: + port->wPortChange &= ~PORT_STAT_C_SUSPEND; + break; + case PORT_C_CONNECTION: + port->wPortChange &= ~PORT_STAT_C_CONNECTION; + break; + case PORT_C_OVERCURRENT: + port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; + break; + case PORT_C_RESET: + port->wPortChange &= ~PORT_STAT_C_RESET; + break; + default: + goto fail; + } + ret = 0; + } + break; + case GetHubDescriptor: + { + unsigned int n, limit, var_hub_size = 0; + memcpy(data, qemu_hub_hub_descriptor, + sizeof(qemu_hub_hub_descriptor)); + data[2] = s->nb_ports; + + /* fill DeviceRemovable bits */ + limit = ((s->nb_ports + 1 + 7) / 8) + 7; + for (n = 7; n < limit; n++) { + data[n] = 0x00; + var_hub_size++; + } + + /* fill PortPwrCtrlMask bits */ + limit = limit + ((s->nb_ports + 7) / 8); + for (;n < limit; n++) { + data[n] = 0xff; + var_hub_size++; + } + + ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; + data[0] = ret; + break; + } + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_hub_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBHubState *s = (USBHubState *)dev; + int ret; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + USBHubPort *port; + unsigned int status; + int i, n; + n = (s->nb_ports + 1 + 7) / 8; + if (len == 1) { /* FreeBSD workaround */ + n = 1; + } else if (n > len) { + return USB_RET_BABBLE; + } + status = 0; + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + if (port->wPortChange) + status |= (1 << (i + 1)); + } + if (status != 0) { + for(i = 0; i < n; i++) { + data[i] = status >> (8 * i); + } + ret = n; + } else { + ret = USB_RET_NAK; /* usb11 11.13.1 */ + } + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_hub_broadcast_packet(USBHubState *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + USBHubPort *port; + USBDevice *dev; + int i, ret; + + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { + ret = dev->handle_packet(dev, pid, + devaddr, devep, + data, len); + if (ret != USB_RET_NODEV) { + return ret; + } + } + } + return USB_RET_NODEV; +} + +static int usb_hub_handle_packet(USBDevice *dev, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + USBHubState *s = (USBHubState *)dev; + +#if defined(DEBUG) && 0 + printf("usb_hub: pid=0x%x\n", pid); +#endif + if (dev->state == USB_STATE_DEFAULT && + dev->addr != 0 && + devaddr != dev->addr && + (pid == USB_TOKEN_SETUP || + pid == USB_TOKEN_OUT || + pid == USB_TOKEN_IN)) { + /* broadcast the packet to the devices */ + return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len); + } + return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); +} + +static void usb_hub_handle_destroy(USBDevice *dev) +{ + USBHubState *s = (USBHubState *)dev; + + free(s); +} + +USBDevice *usb_hub_init(int nb_ports) +{ + USBHubState *s; + USBHubPort *port; + int i; + + if (nb_ports > MAX_PORTS) + return NULL; + s = (USBHubState *)qemu_mallocz(sizeof(USBHubState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_hub_handle_packet; + + /* generic USB device init */ + s->dev.handle_reset = usb_hub_handle_reset; + s->dev.handle_control = usb_hub_handle_control; + s->dev.handle_data = usb_hub_handle_data; + s->dev.handle_destroy = usb_hub_handle_destroy; + + strncpy(s->dev.devname, "QEMU USB Hub", sizeof(s->dev.devname)); + + s->nb_ports = nb_ports; + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + port->port.opaque = s; + port->port.index = i; + port->port.attach = usb_hub_attach; + port->wPortStatus = PORT_STAT_POWER; + port->wPortChange = 0; + } + return (USBDevice *)s; +} diff --git a/plugins/USBqemu/qemu-usb/usb-kbd.cpp b/plugins/USBqemu/qemu-usb/usb-kbd.cpp new file mode 100644 index 0000000000..e4aa6b5f46 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb-kbd.cpp @@ -0,0 +1,2115 @@ +/* + * QEMU USB HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +#define USB_MOUSE 1 +#define USB_TABLET 2 + +typedef struct USBKeyboardState { + USBDevice dev; + int keyboard_grabbed; +} USBKeyboardState; + +extern HWND gsWnd; + +#define VK_BASED + +#ifdef VK_BASED +static const uint8_t vk_to_key_code[] = { +0x00, //FAIL: 0x00 +0x00, //FAIL: LMOUSE +0x00, //FAIL: RMOUSE +0x00, //FAIL: Break +0x00, //FAIL: MMOUSE +0x00, //FAIL: X1MOUSE +0x00, //FAIL: X2MOUSE +0x00, //FAIL: 0x00 +0x2A, //OK: Backspace +0x2B, //OK: Tab +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x9C, //OK: Clear +0x28, //FAIL: ENTER +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: SHIFT +0x00, //FAIL: CTRL +0x00, //FAIL: ALT +0x48, //OK: Pause +0x39, //OK: Caps Lock +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +//0x00, //FAIL: 0x00 +//0x00, //FAIL: 0x00 +//0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x29, //FAIL: ESC +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x2C, //OK: Spacebar +#ifdef ENABLE_KEYPAD_Fx +0x4B, //FAIL: PAGE UP +0x4E, //FAIL: PAGE DOWN +0x4D, //OK: End +0x4A, //OK: Home +0x50, //FAIL: LEFT ARROW +0x52, //FAIL: UP ARROW +0x4F, //FAIL: RIGHT ARROW +0x51, //FAIL: DOWN ARROW +0x77, //OK: Select +0x00, //FAIL: PRINT +0x74, //OK: Execute +0x46, //FAIL: PRINT SCREEN +0x49, //FAIL: INS +0x4C, //FAIL: DEL +0x75, //OK: Help VK_HOME +#else +0x00, //FAIL: PAGE UP +0x00, //FAIL: PAGE DOWN +0x00, //OK: End +0x00, //OK: Home +0x00, //FAIL: LEFT ARROW +0x00, //FAIL: UP ARROW +0x00, //FAIL: RIGHT ARROW +0x00, //FAIL: DOWN ARROW +0x00, //OK: Select +0x00, //FAIL: PRINT +0x00, //OK: Execute +0x00, //FAIL: PRINT SCREEN +0x00, //FAIL: INS +0x00, //FAIL: DEL +0x00, //OK: Help VK_HOME +#endif +0x27, //OK: 0 +0x1E, //OK: 1 +0x1F, //OK: 2 +0x20, //OK: 3 +0x21, //OK: 4 +0x22, //OK: 5 +0x23, //OK: 6 +0x24, //OK: 7 +0x25, //OK: 8 +0x26, //OK: 9 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: not found +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x04, //OK: A +0x05, //OK: B +0x06, //OK: C +0x07, //OK: D +0x08, //OK: E +0x09, //OK: F +0x0A, //OK: G +0x0B, //OK: H +0x0C, //OK: I +0x0D, //OK: J +0x0E, //OK: K +0x0F, //OK: L +0x10, //OK: M +0x11, //OK: N +0x12, //OK: O +0x13, //OK: P +0x14, //OK: Q +0x15, //OK: R +0x16, //OK: S +0x17, //OK: T +0x18, //OK: U +0x19, //OK: V +0x1A, //OK: W +0x1B, //OK: X +0x1C, //OK: Y +0x1D, //OK: Z +#ifdef ENABLE_KEYPAD_Fx +0xE3, //OK: LGUI +0xE7, //OK: RGUI +0x65, //OK: Application +0x00, //FAIL: 0x00 +0x00, //FAIL: SLEEP +0x62, //OK: Keypad 0 +0x59, //OK: Keypad 1 +0x5A, //OK: Keypad 2 +0x5B, //OK: Keypad 3 +0x5C, //OK: Keypad 4 +0x5D, //OK: Keypad 5 +0x5E, //OK: Keypad 6 +0x5F, //OK: Keypad 7 +0x60, //OK: Keypad 8 +0x61, //OK: Keypad 9 +0x55, //OK: Keypad * +0x57, //OK: Keypad + +0x9F, //OK: Separator +0x56, //OK: Keypad - +0x63, //OK: Keypad . +0x54, //OK: Keypad / +0x3A, //OK: F1 +0x3B, //OK: F2 +0x3C, //OK: F3 +0x3D, //OK: F4 +0x3E, //OK: F5 +0x3F, //OK: F6 +0x40, //OK: F7 +0x41, //OK: F8 +0x42, //OK: F9 +0x43, //OK: F10 +0x44, //OK: F11 +0x45, //OK: F12 +0x68, //OK: F13 +0x69, //OK: F14 +0x6A, //OK: F15 +0x6B, //OK: F16 +0x6C, //OK: F17 +0x6D, //OK: F18 +0x6E, //OK: F19 +0x6F, //OK: F20 +0x70, //OK: F21 +0x71, //OK: F22 +0x72, //OK: F23 +0x73, //OK: F24 +#else +0x00, //OK: LGUI +0x00, //OK: RGUI +0x00, //OK: Application +0x00, //FAIL: 0x00 +0x00, //FAIL: SLEEP +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, +#endif +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x83, //OK: NUM LOCK +0x47, //OK: Scroll Lock +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0xE1, //OK: LSHIFT +0xE5, //OK: RSHIFT +0xE0, //OK: LCONTROL +0xE4, //OK: RCONTROL +0xE3, //OK: LGUI +0xE7, //OK: RGUI +0x00, //FAIL: Windows 2000/XP: Browser Back +0x00, //FAIL: Windows 2000/XP: Browser Forward +0x00, //FAIL: Windows 2000/XP: Browser Refresh +0x00, //FAIL: Windows 2000/XP: Browser Stop +0x00, //FAIL: Windows 2000/XP: Browser Search +0x00, //FAIL: Windows 2000/XP: Browser Favorites +0x00, //FAIL: Windows 2000/XP: Browser Start and Home +0x00, //FAIL: Windows 2000/XP: Volume Mute +0x00, //FAIL: Windows 2000/XP: Volume Down +0x00, //FAIL: Windows 2000/XP: Volume Up +0x00, //FAIL: Windows 2000/XP: Next Track +0x00, //FAIL: Windows 2000/XP: Previous Track +0x00, //FAIL: Windows 2000/XP: Stop Media +0x00, //FAIL: Windows 2000/XP: Play/Pause Media +0x00, //FAIL: Windows 2000/XP: Start Mail +0x00, //FAIL: Windows 2000/XP: Select Media +0x00, //FAIL: Windows 2000/XP: Start Application 1 +0x00, //FAIL: Windows 2000/XP: Start Application 2 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x33, //FAIL: Windows 2000/XP: For the US standard keyboard, the ';:' key +0x2E, //FAIL: Windows 2000/XP: For any country/region, the '+' +0x36, //FAIL: Windows 2000/XP: For any country/region, the ',' +0x2D, //FAIL: Windows 2000/XP: For any country/region, the '-' +0x37, //FAIL: Windows 2000/XP: For any country/region, the '.' +0x38, //FAIL: Windows 2000/XP: For the US standard keyboard, the '/?' key +0x35, //FAIL: Windows 2000/XP: For the US standard keyboard, the '`~' key +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: not found +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x2F, //FAIL: Windows 2000/XP: For the US standard keyboard, the '[{' key +0x31, //FAIL: Windows 2000/XP: For the US standard keyboard, the '\|' key +0x30, //FAIL: Windows 2000/XP: For the US standard keyboard, the ']}' key +0x34, //FAIL: Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key +0x00, //FAIL: Used for miscellaneous characters; it can vary byboard. +0x00, //FAIL: Reserved +0x00, //FAIL: OEM specific +0x32, //FAIL: Windows 2000/XP: Either the angle bracket or the backslash key on the RT 102-key keyboard +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCEE6 +0x00, //FAIL: OEM specific +0x00, //FAIL: Windows 2000/XP: Used to pass Unicode characters as if they E8 +0x00, //FAIL: Unassigned +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: 0x00 +0x00, //FAIL: Attn +0xA3, //OK: CrSel +0xA4, //OK: ExSel +0x00, //FAIL: Erase EOF +0x00, //FAIL: Play +0x00, //FAIL: Zoom +0x00, //FAIL: Reserved +0x00, //FAIL: PA1 +0x9C, //OK: Clear +0x00, //FAIL: 0x00 +}; +#else +# ifdef ENABLE_KEYPAD_Fx +static const unsigned char scan_to_usb[] = { +0x00,0x29,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x2f,0x30,0x2a,0x2b, +0x14,0x1a,0x08,0x15,0x17,0x1c,0x18,0x0c,0x12,0x13,0x33,0x2e,0x28,0xe0,0x04,0x16, +0x07,0x09,0x0a,0x0b,0x0d,0x0e,0x0f,0x35,0x34,0x31,0xe1,0x38,0x1d,0x1b,0x06,0x19, +0x05,0x11,0x10,0x36,0x37,0x2d,0xe5,0x55,0xe3,0x2c,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, +0x3f,0x40,0x41,0x42,0x43,0x83,0x47,0x5f,0x60,0x61,0x56,0x5c,0x5d,0x5e,0x57,0x59, +0x5a,0x5b,0x62,0x63,0x46,0x00,0x32,0x44,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x75,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe4,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x54,0x00,0x00,0xe7,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe7,0x65,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +# else +static const unsigned char scan_to_usb[] = { +0x00,0x29,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x2f,0x30,0x2a,0x2b, +0x14,0x1a,0x08,0x15,0x17,0x1c,0x18,0x0c,0x12,0x13,0x33,0x2e,0x28,0xe0,0x04,0x16, +0x07,0x09,0x0a,0x0b,0x0d,0x0e,0x0f,0x35,0x34,0x31,0xe1,0x38,0x1d,0x1b,0x06,0x19, +0x05,0x11,0x10,0x36,0x37,0x2d,0xe5,0x00,0xe3,0x2c,0x39,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x83,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x46,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe4,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe7,0x65,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +# endif +#endif + +/* mostly the same values as the Bochs USB Keyboard device */ +static const uint8_t qemu_keyboard_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x00, /* u16 bcdUSB; v1.0 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x27, 0x06, /* u16 idVendor; */ + 0x01, 0x00, /* u16 idProduct; */ + 0x00, 0x00, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_keyboard_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x01, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 50, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x03, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_keyboard_hid_report_descriptor[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard Left Control) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (Num Lock) + 0x29, 0x05, // USAGE_MAXIMUM (Kana) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x65, // LOGICAL_MAXIMUM (101) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len) +{ + static unsigned char keys[256]; + int i,l; + + if (!s->keyboard_grabbed) { + //qemu_add_keyboard_event_handler(usb_keyboard_event, s, 0); + s->keyboard_grabbed = 1; + } + + if(gsWnd != GetForegroundWindow()) + { + for(int i=0;i<256;i++) + { + keys[i] = 0; + } + } + else + { + for(int i=0;i<256;i++) + { + keys[i] = GetAsyncKeyState(i)>>8; + } + } + + l=1; + l=2; + buf[0] = 0; + if(keys[VK_LCONTROL]>>7) buf[0]|=(1<<0); + if(keys[VK_LSHIFT]>>7) buf[0]|=(1<<1); + if(keys[VK_LMENU]>>7) buf[0]|=(1<<2); //ALT key + if(keys[VK_LWIN]>>7) buf[0]|=(1<<3); + if(keys[VK_RCONTROL]>>7) buf[0]|=(1<<4); + if(keys[VK_RSHIFT]>>7) buf[0]|=(1<<5); + if(keys[VK_RMENU]>>7) buf[0]|=(1<<6); + if(keys[VK_RWIN]>>7) buf[0]|=(1<<7); + + buf[1] = 0; //reserved byte + + int k=0; + for(int i=0;i<256;i++) + { + if(keys[i]>>7) //if pressed + { + if(l==8) + { + buf[1]=buf[1]=buf[1]=buf[1]=buf[1]=buf[1]=buf[1]=buf[1]=1; + } +#ifdef VK_BASED + int uc = vk_to_key_code[i]; +#else + int sc = MapVirtualKey(i,MAPVK_VK_TO_VSC_EX); + if((sc>>8)!=0) + sc=(sc&0x1FF)+256; + int uc = scan_to_usb[sc]; + printf("// %08x->%02x ",i,sc); + k++; +#endif + if((uc>0)&&(uc<=0x65)) // + buf[l++]=uc; + } + + } + if(k) printf("\n"); + + /*if(l>=1) + { + while(l<8) + buf[l++]=0; + printf("KEYS: %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + l=l; + }*/ + + while(l<7) + buf[l++]=0; + + return l; +} + +static void usb_keyboard_handle_reset(USBDevice *dev) +{ + USBKeyboardState *s = (USBKeyboardState *)dev; +} + +static int usb_keyboard_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBKeyboardState *s = (USBKeyboardState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_keyboard_dev_descriptor, + sizeof(qemu_keyboard_dev_descriptor)); + ret = sizeof(qemu_keyboard_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, qemu_keyboard_config_descriptor, + sizeof(qemu_keyboard_config_descriptor)); + ret = sizeof(qemu_keyboard_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "1"); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "Generic USB Keyboard"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "PCSX2/QEMU"); + break; + case 4: + ret = set_usb_string(data, "HID Keyboard"); + break; + case 5: + ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case 0x22: + memcpy(data, qemu_keyboard_hid_report_descriptor, + sizeof(qemu_keyboard_hid_report_descriptor)); + ret = sizeof(qemu_keyboard_hid_report_descriptor); + break; + default: + goto fail; + } + break; + case GET_REPORT: + ret = usb_keyboard_poll(s, data, length); + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_keyboard_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBKeyboardState *s = (USBKeyboardState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + ret = usb_keyboard_poll(s, data, len); + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static void usb_keyboard_handle_destroy(USBDevice *dev) +{ + USBKeyboardState *s = (USBKeyboardState *)dev; + + //qemu_add_keyboard_event_handler(NULL, NULL, 0); + free(s); +} + +USBDevice *usb_keyboard_init(void) +{ + USBKeyboardState *s; + + s = (USBKeyboardState *)malloc(sizeof(USBKeyboardState)); + if (!s) + return NULL; + memset(s,0,sizeof(USBKeyboardState)); + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_keyboard_handle_reset; + s->dev.handle_control = usb_keyboard_handle_control; + s->dev.handle_data = usb_keyboard_handle_data; + s->dev.handle_destroy = usb_keyboard_handle_destroy; + + strncpy(s->dev.devname, "Generic USB Keyboard", sizeof(s->dev.devname)); + + return (USBDevice *)s; +} + +#if 0 +#define PS2KBD_VERSION 0x100 + +#define USB_SUBCLASS_BOOT 1 +#define USB_HIDPROTO_KEYBOARD 1 + +#define PS2KBD_MAXDEV 2 +#define PS2KBD_MAXKEYS 6 + +#define PS2KBD_DEFLINELEN 4096 +#define PS2KBD_DEFREPEATRATE 100 +/* Default repeat rate in milliseconds */ +#define PS2KBD_REPEATWAIT 1000 +/* Number of milliseconds to wait before starting key repeat */ +#define USB_KEYB_NUMLOCK 0x53 +#define USB_KEYB_CAPSLOCK 0x39 +#define USB_KEYB_SCRLOCK 0x47 + +#define USB_KEYB_NUMPAD_START 0x54 +#define USB_KEYB_NUMPAD_END 0x63 + +#define SEMA_ZERO -419 +#define SEMA_DELETED -425 + +int ps2kbd_init(); +void ps2kbd_config_set(int resultCode, int bytes, void *arg); +void ps2kbd_idlemode_set(int resultCode, int bytes, void *arg); +void ps2kbd_data_recv(int resultCode, int bytes, void *arg); +int ps2kbd_probe(int devId); +int ps2kbd_connect(int devId); +int ps2kbd_disconnect(int devId); +void usb_getstring(int endp, int index, char *desc); + +typedef struct _kbd_data_recv + +{ + u8 mod_keys; + u8 reserved; + u8 keycodes[PS2KBD_MAXKEYS]; +} kbd_data_recv; + +typedef struct _keyb_dev + +{ + int configEndp; + int dataEndp; + int packetSize; + int devId; + int interfaceNo; /* Holds the interface number selected on this device */ + char repeatkeys[2]; + u32 eventmask; + u8 ledStatus; /* Maintains state on the led status */ + kbd_data_recv oldData; + kbd_data_recv data; /* Holds the data for the transfers */ +} kbd_dev; + +/* Global Variables */ + +int kbd_readmode; +int kbd_blocking; +u32 kbd_repeatrate; +kbd_dev *devices[PS2KBD_MAXDEV]; /* Holds a list of current devices */ +int dev_count; +UsbDriver kbd_driver = { NULL, NULL, "PS2Kbd", ps2kbd_probe, ps2kbd_connect, ps2kbd_disconnect }; +u8 *lineBuffer; +u32 lineStartP, lineEndP; +int lineSema; +int bufferSema; +u32 lineSize; +u8 keymap[PS2KBD_KEYMAP_SIZE]; /* Normal key map */ +u8 shiftkeymap[PS2KBD_KEYMAP_SIZE]; /* Shifted key map */ +u8 keycap[PS2KBD_KEYMAP_SIZE]; /* Does this key get shifted by capslock ? */ +u8 special_keys[PS2KBD_KEYMAP_SIZE]; +u8 control_map[PS2KBD_KEYMAP_SIZE]; +u8 alt_map[PS2KBD_KEYMAP_SIZE]; +//static struct fileio_driver kbd_fdriver; +iop_device_t kbd_filedrv; +u8 keyModValue[8] = { 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7 }; +int repeat_tid; +int eventid; /* Id of the repeat event */ + +int _start () +{ + FlushDcache(); + + ps2kbd_init(); + + printf("PS2KBD - USB Keyboard Library\n"); + + return 0; + +} + +int ps2kbd_probe(int devId) + +{ + UsbDeviceDescriptor *dev; + UsbConfigDescriptor *conf; + UsbInterfaceDescriptor *intf; + UsbEndpointDescriptor *endp; + //UsbStringDescriptor *str; + + if(dev_count >= PS2KBD_MAXDEV) + { + printf("ERROR: Maximum keyboard devices reached\n"); + return 0; + } + + //printf("PS2Kbd_probe devId %d\n", devId); + + dev = UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); /* Get device descriptor */ + if(!dev) + { + printf("ERROR: Couldn't get device descriptor\n"); + return 0; + } + + //printf("Device class %d, Size %d, Man %d, Product %d Cpnfigurations %d\n", dev->bDeviceClass, dev->bMaxPacketSize0, dev->iManufacturer, dev->iProduct, dev->bNumConfigurations); + /* Check that the device class is specified in the interfaces and it has at least one configuration */ + if((dev->bDeviceClass != USB_CLASS_PER_INTERFACE) || (dev->bNumConfigurations < 1)) + { + //printf("This is not the droid you're looking for\n"); + return 0; + } + + conf = UsbGetDeviceStaticDescriptor(devId, dev, USB_DT_CONFIG); + if(!conf) + { + printf("ERROR: Couldn't get configuration descriptor\n"); + return 0; + } + //printf("Config Length %d Total %d Interfaces %d\n", conf->bLength, conf->wTotalLength, conf->bNumInterfaces); + + if((conf->bNumInterfaces < 1) || (conf->wTotalLength < (sizeof(UsbConfigDescriptor) + sizeof(UsbInterfaceDescriptor)))) + { + printf("ERROR: No interfaces available\n"); + return 0; + } + + intf = (UsbInterfaceDescriptor *) ((char *) conf + conf->bLength); /* Get first interface */ +/* printf("Interface Length %d Endpoints %d Class %d Sub %d Proto %d\n", intf->bLength, */ +/* intf->bNumEndpoints, intf->bInterfaceClass, intf->bInterfaceSubClass, */ +/* intf->bInterfaceProtocol); */ + + if((intf->bInterfaceClass != USB_CLASS_HID) || (intf->bInterfaceSubClass != USB_SUBCLASS_BOOT) || + (intf->bInterfaceProtocol != USB_HIDPROTO_KEYBOARD) || (intf->bNumEndpoints < 1)) + + { + //printf("We came, we saw, we told it to fuck off\n"); + return 0; + } + + endp = (UsbEndpointDescriptor *) ((char *) intf + intf->bLength); + endp = (UsbEndpointDescriptor *) ((char *) endp + endp->bLength); /* Go to the data endpoint */ + + //printf("Endpoint 1 Addr %d, Attr %d, MaxPacket %d\n", endp->bEndpointAddress, endp->bmAttributes, endp->wMaxPacketSizeLB); + + if(((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) || + ((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)) + { + printf("ERROR: Endpoint not interrupt type and/or an input\n"); + return 0; + } + + printf("PS2KBD: Found a keyboard device\n"); + + return 1; +} + +int ps2kbd_connect(int devId) + +{ + /* Assume we can only get here if we have already checked the device is kosher */ + + UsbDeviceDescriptor *dev; + UsbConfigDescriptor *conf; + UsbInterfaceDescriptor *intf; + UsbEndpointDescriptor *endp; + kbd_dev *currDev; + int devLoop; + + //printf("PS2Kbd_connect devId %d\n", devId); + + dev = UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); /* Get device descriptor */ + if(!dev) + { + printf("ERROR: Couldn't get device descriptor\n"); + return 1; + } + + conf = UsbGetDeviceStaticDescriptor(devId, dev, USB_DT_CONFIG); + if(!conf) + { + printf("ERROR: Couldn't get configuration descriptor\n"); + return 1; + } + + intf = (UsbInterfaceDescriptor *) ((char *) conf + conf->bLength); /* Get first interface */ + endp = (UsbEndpointDescriptor *) ((char *) intf + intf->bLength); + endp = (UsbEndpointDescriptor *) ((char *) endp + endp->bLength); /* Go to the data endpoint */ + + for(devLoop = 0; devLoop < PS2KBD_MAXDEV; devLoop++) + { + if(devices[devLoop] == NULL) + { + break; + } + } + + if(devLoop == PS2KBD_MAXDEV) + { + /* How the f*** did we end up here ??? */ + printf("ERROR: Device Weirdness!!\n"); + return 1; + } + + currDev = (kbd_dev *) AllocSysMemory(0, sizeof(kbd_dev), NULL); + if(!currDev) + { + printf("ERROR: Couldn't allocate a device point for the kbd\n"); + return 1; + } + + devices[devLoop] = currDev; + memset(currDev, 0, sizeof(kbd_dev)); + currDev->configEndp = UsbOpenEndpoint(devId, NULL); + currDev->dataEndp = UsbOpenEndpoint(devId, endp); + currDev->packetSize = endp->wMaxPacketSizeLB | ((int) endp->wMaxPacketSizeHB << 8); + currDev->eventmask = (1 << devLoop); + if(currDev->packetSize > sizeof(kbd_data_recv)) + { + currDev->packetSize = sizeof(kbd_data_recv); + } + + if(dev->iManufacturer != 0) + { + usb_getstring(currDev->configEndp, dev->iManufacturer, "Keyboard Manufacturer"); + } + + if(dev->iProduct != 0) + { + usb_getstring(currDev->configEndp, dev->iProduct, "Keyboard Product"); + } + + currDev->devId = devId; + currDev->interfaceNo = intf->bInterfaceNumber; + currDev->ledStatus = 0; + + UsbSetDevicePrivateData(devId, currDev); /* Set the index for the device data */ + + //printf("Configuration value %d\n", conf->bConfigurationValue); + UsbSetDeviceConfiguration(currDev->configEndp, conf->bConfigurationValue, ps2kbd_config_set, currDev); + + dev_count++; /* Increment device count */ + printf("PS2KBD: Connected device\n"); + + return 0; +} + +int ps2kbd_disconnect(int devId) + +{ + int devLoop; + //printf("PS2Kbd_disconnect devId %d\n", devId); + + for(devLoop = 0; devLoop < PS2KBD_MAXDEV; devLoop++) + { + if((devices[devLoop]) && (devices[devLoop]->devId == devId)) + { + dev_count--; + FreeSysMemory(devices[devLoop]); + devices[devLoop] = NULL; + printf("PS2KBD: Disconnected device\n"); + break; + } + } + + return 0; +} + +typedef struct _string_descriptor + +{ + u8 buf[200]; + char *desc; +} string_descriptor; + +void ps2kbd_getstring_set(int resultCode, int bytes, void *arg) + +{ + UsbStringDescriptor *str = (UsbStringDescriptor *) arg; + string_descriptor *strBuf = (string_descriptor *) arg; + char string[50]; + int strLoop; + +/* printf("=========getstring=========\n"); */ + +/* printf("PS2KEYBOARD: GET_DESCRIPTOR res %d, bytes %d, arg %p\n", resultCode, bytes, arg); */ + + if(resultCode == USB_RC_OK) + { + memset(string, 0, 50); + for(strLoop = 0; strLoop < ((bytes - 2) / 2); strLoop++) + { + string[strLoop] = str->wData[strLoop] & 0xFF; + } + printf("PS2KBD %s: %s\n", strBuf->desc, string); + } + + FreeSysMemory(arg); +} + +void usb_getstring(int endp, int index, char *desc) + +{ + u8 *data; + string_descriptor *str; + int ret; + + data = (u8 *) AllocSysMemory(0, sizeof(string_descriptor), NULL); + str = (string_descriptor *) data; + + if(data != NULL) + { + str->desc = desc; + ret = UsbControlTransfer(endp, 0x80, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | index, + 0, sizeof(string_descriptor) - 4, data, ps2kbd_getstring_set, data); + if(ret != USB_RC_OK) + { + printf("PS2KBD: Error sending string descriptor request\n"); + FreeSysMemory(data); + } + } +} + +void ps2kbd_config_set(int resultCode, int bytes, void *arg) + /* Called when we have finished choosing our configuration */ + +{ + kbd_dev *dev; + + if(resultCode != USB_RC_OK) + { + printf("PS2KEYBOARD: Configuration set error res %d, bytes %d, arg %p\n", resultCode, bytes, arg); + return; + } + + //printf("PS2KEYBOARD: Configuration set res %d, bytes %d, arg %p\n", resultCode, bytes, arg); + /* Do a interrupt data transfer */ + + dev = (kbd_dev *) arg; + if(dev != NULL) + { + int ret; + + ret = UsbControlTransfer(dev->configEndp, 0x21, USB_REQ_SET_IDLE, 0, dev->interfaceNo, 0, NULL, ps2kbd_idlemode_set, arg); + } +} + +void ps2kbd_idlemode_set(int resultCode, int bytes, void *arg) + +{ + kbd_dev *dev; + + + + if(resultCode != USB_RC_OK) + { + printf("PS2KBD: Idlemode set error res %d, bytes %d, arg %p\n", resultCode, bytes, arg); + return; + } + + dev = (kbd_dev *) arg; + if(dev != NULL) + { + int ret; + + ret = UsbInterruptTransfer(dev->dataEndp, &dev->data, dev->packetSize, ps2kbd_data_recv, arg); + } +} + +void ps2kbd_led_set(int resultCode, int bytes, void *arg) + +{ + //printf("LED Set\n"); +} + +void ps2kbd_build_uniquekeys(u8 *res, const u8 *new, const u8 *old) + + /* Builds a list of unique keys */ + +{ + int loopNew, loopOld; + int loopRes = 0; + int foundKey; + + for(loopNew = 0; loopNew < PS2KBD_MAXKEYS; loopNew++) + { + if(new[loopNew] != 0) + { + foundKey = 0; + for(loopOld = 0; loopOld < PS2KBD_MAXKEYS; loopOld++) + { + if(new[loopNew] == old[loopOld]) + { + foundKey = 1; + break; + } + } + if(!foundKey) + { + res[loopRes++] = new[loopNew]; + } + } + } +} + +u32 ps2kbd_repeathandler(void *arg) + +{ + kbd_dev *dev = arg; + iop_sys_clock_t t; + //printf("Repeat handler\n"); + + iSetEventFlag(eventid, dev->eventmask); + + USec2SysClock(kbd_repeatrate * 1000, &t); + iSetAlarm(&t, (void *)ps2kbd_repeathandler, arg); + + return t.hi; +} + +void ps2kbd_getkeys(u8 keyMods, u8 ledStatus, const u8 *keys, kbd_dev *dev) + +{ + int loopKey; + int tempPos = 0; + int byteCount = 0; + u8 currChars[2]; + + if(lineStartP < lineEndP) + { + tempPos = lineStartP + lineSize; + } + else + { + tempPos = lineStartP; + } + + for(loopKey = 0; loopKey < PS2KBD_MAXKEYS; loopKey++) + { + u8 currKey = keys[loopKey]; + + currChars[0] = 0; + currChars[1] = 0; + + if(lineEndP == (tempPos - 1)) + { + break; + } + + if(currKey) /* If this is a valid key */ + { + if((currKey >= USB_KEYB_NUMPAD_START) && (currKey <= USB_KEYB_NUMPAD_END)) + /* Handle numpad specially */ + { + if(ledStatus & PS2KBD_LED_NUMLOCK) + { + if(keymap[currKey]) + { + currChars[0] = keymap[currKey]; + } + } + else + { + if(special_keys[currKey]) + { + currChars[0] = PS2KBD_ESCAPE_KEY; + currChars[1] = special_keys[currKey]; + } + else if(keymap[currKey] != '5') /* Make sure this isnt a 5 key :) */ + { + currChars[0] = keymap[currKey]; + } + } + } + else if(special_keys[currKey]) /* This is a special key */ + { + currChars[0] = PS2KBD_ESCAPE_KEY; + currChars[1] = special_keys[currKey]; + } + else if(keyMods & PS2KBD_CTRL) /* CTRL */ + { + if(control_map[currKey]) + { + currChars[0] = control_map[currKey]; + } + } + else if(keyMods & PS2KBD_ALT) /* ALT */ + { + if(alt_map[currKey]) + { + currChars[0] = alt_map[currKey]; + } + } + else if(keyMods & PS2KBD_SHIFT) /* SHIFT */ + { + if((ledStatus & PS2KBD_LED_CAPSLOCK) && (keycap[currKey])) + { + currChars[0] = keymap[currKey]; + } + else + { + currChars[0] = shiftkeymap[currKey]; + } + } + else /* Normal key */ + { + if(keymap[keys[loopKey]]) + { + if((ledStatus & PS2KBD_LED_CAPSLOCK) && (keycap[currKey])) + { + currChars[0] = shiftkeymap[currKey]; + } + else + { + currChars[0] = keymap[currKey]; + } + } + } + } + + if((currChars[0] == PS2KBD_ESCAPE_KEY) && (currChars[1] != 0)) + { + if(lineEndP != (tempPos - 2)) + { + lineBuffer[lineEndP++] = currChars[0]; + lineEndP %= lineSize; + lineBuffer[lineEndP++] = currChars[1]; + lineEndP %= lineSize; + byteCount += 2; + } + dev->repeatkeys[0] = currChars[0]; + dev->repeatkeys[1] = currChars[1]; + } + else if(currChars[0] != 0) + { + lineBuffer[lineEndP++] = currChars[0]; + lineEndP %= lineSize; + byteCount++; + dev->repeatkeys[0] = currChars[0]; + dev->repeatkeys[1] = 0; + } + } + + if(byteCount > 0) + { + iop_sys_clock_t t; + /* Set alarm to do repeat rate */ + //printf("repeatkeys %d %d\n", kbd_repeatkeys[0], kbd_repeatkeys[1]); + USec2SysClock(PS2KBD_REPEATWAIT * 1000, &t); + SetAlarm(&t, (void *)ps2kbd_repeathandler, dev); + } + + for(loopKey = 0; loopKey < byteCount; loopKey++) /* Signal the sema to indicate data */ + { + SignalSema(bufferSema); + } + +/* lineBuffer[PS2KBD_DEFLINELEN - 1] = 0; */ +/* printf(lineBuffer); */ + //printf("lineStart %d, lineEnd %d\n", lineStartP, lineEndP); +} + + +void ps2kbd_getkeys_raw(u8 newKeyMods, u8 oldKeyMods, u8 *new, const u8 *old) + +{ + int loopKey; + u8 currKey; + u8 keyMods = newKeyMods ^ oldKeyMods; + u8 keyModsMap = newKeyMods & keyMods; + int tempPos = 0; + int byteCount = 0; + + if(lineStartP < lineEndP) + { + tempPos = lineStartP + lineSize; + } + else + { + tempPos = lineStartP; + } + + for(loopKey = 0; loopKey < 8; loopKey++) + { + int currMod = (1 << loopKey); + if(keyMods & currMod) + { + if(lineEndP == (tempPos - 2)) + { + return; + } + + currKey = keyModValue[loopKey]; + + if(keyModsMap & currMod) /* If key pressed */ + { + lineBuffer[lineEndP++] = PS2KBD_RAWKEY_DOWN; + //printf("Key down\n"); + } + else + { + lineBuffer[lineEndP++] = PS2KBD_RAWKEY_UP; + //printf("Key up\n"); + } + + lineEndP %= lineSize; + lineBuffer[lineEndP++] = currKey; + lineEndP %= lineSize; + byteCount += 2; + //printf("Key %d\n", currKey); + } + } + + for(loopKey = 0; loopKey < PS2KBD_MAXKEYS; loopKey++) + { + if(lineEndP == (tempPos - 2)) + { + return; + } + + if(new[loopKey] != 0) + { + lineBuffer[lineEndP++] = PS2KBD_RAWKEY_DOWN; + lineEndP %= lineSize; + lineBuffer[lineEndP++] = new[loopKey]; + lineEndP %= lineSize; + byteCount += 2; + //printf("Key down\nKey %d\n", new[loopKey]); + } + + } + + for(loopKey = 0; loopKey < PS2KBD_MAXKEYS; loopKey++) + { + if(lineEndP == (tempPos - 2)) + { + return; + } + + if(old[loopKey] != 0) + { + lineBuffer[lineEndP++] = PS2KBD_RAWKEY_UP; + lineEndP %= lineSize; + lineBuffer[lineEndP++] = old[loopKey]; + lineEndP %= lineSize; + byteCount += 2; + //printf("Key up\nKey %d\n", old[loopKey]); + } + + } + + for(loopKey = 0; loopKey < byteCount; loopKey++) /* Signal the sema for the number of bytes read */ + { + SignalSema(bufferSema); + } +} + +void ps2kbd_data_recv(int resultCode, int bytes, void *arg) + +{ + kbd_dev *dev; + int ret; + int phantom; + int loop; + + if(resultCode != USB_RC_OK) + { + printf("PS2KEYBOARD: Data Recv set res %d, bytes %d, arg %p\n", resultCode, bytes, arg); + return; + } + + //printf("PS2KBD: Data Recv set res %d, bytes %d, arg %p\n", resultCode, bytes, arg); + + dev = (kbd_dev *) arg; + if(dev == NULL) + { + printf("PS2KBD: dev == NULL\n"); + return; + } + +/* printf("PS2KBD Modifiers %02X, Keys ", dev->data.mod_keys); */ +/* for(loop = 0; loop < PS2KBD_MAXKEYS; loop++) */ +/* { */ +/* printf("%02X ", dev->data.keycodes[loop]); */ +/* } */ +/* printf("\n"); */ + + CancelAlarm((void *)ps2kbd_repeathandler, dev); /* Make sure repeat alarm is cancelled */ + + /* Check for phantom states */ + phantom = 1; + for(loop = 0; loop < PS2KBD_MAXKEYS; loop++) + { + if(dev->data.keycodes[loop] != 1) + { + phantom = 0; + break; + } + } + + if(!phantom) /* If not in a phantom state */ + { + u8 uniqueKeys[PS2KBD_MAXKEYS]; + u8 missingKeys[PS2KBD_MAXKEYS]; + int loopKey; + + memset(uniqueKeys, 0, PS2KBD_MAXKEYS); + memset(missingKeys, 0, PS2KBD_MAXKEYS); + ps2kbd_build_uniquekeys(uniqueKeys, dev->data.keycodes, dev->oldData.keycodes); + ps2kbd_build_uniquekeys(missingKeys, dev->oldData.keycodes, dev->data.keycodes); + /* Build new and missing key lists */ + +/* printf("Unique keys : "); */ +/* for(loopKey = 0; loopKey < PS2KBD_MAXKEYS; loopKey++) */ +/* { */ +/* printf("%02X ", uniqueKeys[loopKey]); */ +/* } */ +/* printf("\n"); */ + +/* printf("Missing keys : "); */ +/* for(loopKey = 0; loopKey < PS2KBD_MAXKEYS; loopKey++) */ +/* { */ +/* printf("%02X ", missingKeys[loopKey]); */ +/* } */ +/* printf("\n"); */ + + if(kbd_readmode == PS2KBD_READMODE_NORMAL) + { + u8 ledStatus; + + ledStatus = dev->ledStatus; + //printf("ledStatus %02X\n", ledStatus); + + for(loopKey = 0; loopKey < PS2KBD_MAXKEYS; loopKey++) /* Process key codes */ + { + switch(uniqueKeys[loopKey]) + { + case USB_KEYB_NUMLOCK : + ledStatus ^= PS2KBD_LED_NUMLOCK; + uniqueKeys[loopKey] = 0; + break; + case USB_KEYB_CAPSLOCK : + ledStatus ^= PS2KBD_LED_CAPSLOCK; + uniqueKeys[loopKey] = 0; + break; + case USB_KEYB_SCRLOCK : + ledStatus ^= PS2KBD_LED_SCRLOCK; + uniqueKeys[loopKey] = 0; + break; + } + } + + if(ledStatus != dev->ledStatus) + { + dev->ledStatus = ledStatus & PS2KBD_LED_MASK; + //printf("LEDS %02X\n", dev->ledStatus); + /* Call Set LEDS */ + UsbControlTransfer(dev->configEndp, 0x21, USB_REQ_SET_REPORT, 0x200, + dev->interfaceNo, 1, &dev->ledStatus, ps2kbd_led_set, arg); + } + + WaitSema(lineSema); /* Make sure no other thread is going to manipulate the buffer */ + ps2kbd_getkeys(dev->data.mod_keys, dev->ledStatus, uniqueKeys, dev); /* read in remaining keys */ + SignalSema(lineSema); + } + else /* RAW Mode */ + { + WaitSema(lineSema); + ps2kbd_getkeys_raw(dev->data.mod_keys, dev->oldData.mod_keys, uniqueKeys, missingKeys); + SignalSema(lineSema); + } + + memcpy(&dev->oldData, &dev->data, sizeof(kbd_data_recv)); + } + + ret = UsbInterruptTransfer(dev->dataEndp, &dev->data, dev->packetSize, ps2kbd_data_recv, arg); +} + +void flushbuffer() + +{ + iop_sema_t s; + + lineStartP = 0; + lineEndP = 0; + memset(lineBuffer, 0, lineSize); + + DeleteSema(bufferSema); + s.initial = 0; + s.max = lineSize; + s.option = 0; + s.attr = 0; + bufferSema = CreateSema(&s); /* Create a sema to maintain status of readable data */ + + if(bufferSema <= 0) + { + printf("PS2KBD: Error creating buffer sema\n"); + } +} + +void ps2kbd_ioctl_setreadmode(u32 readmode) + +{ + int devLoop; + + if(readmode == kbd_readmode) return; + + if((readmode == PS2KBD_READMODE_NORMAL) || (readmode == PS2KBD_READMODE_RAW)) + { + /* Reset line buffer */ + //printf("ioctl_setreadmode %d\n", readmode); + for(devLoop = 0; devLoop < PS2KBD_MAXDEV; devLoop++) + { + CancelAlarm((void *)ps2kbd_repeathandler, devices[devLoop]); + } + + WaitSema(lineSema); + kbd_readmode = readmode; + flushbuffer(); + SignalSema(lineSema); + } +} + +void ps2kbd_ioctl_setkeymap(kbd_keymap *keymaps) + +{ + //printf("ioctl_setkeymap %p\n", keymaps); + WaitSema(lineSema); /* Lock the input so you dont end up with weird results */ + memcpy(keymap, keymaps->keymap, PS2KBD_KEYMAP_SIZE); + memcpy(shiftkeymap, keymaps->shiftkeymap, PS2KBD_KEYMAP_SIZE); + memcpy(keycap, keymaps->keycap, PS2KBD_KEYMAP_SIZE); + SignalSema(lineSema); +} + +void ps2kbd_ioctl_setctrlmap(u8 *ctrlmap) + +{ + //printf("ioctl_setctrlmap %p\n", ctrlmap); + WaitSema(lineSema); + memcpy(control_map, ctrlmap, PS2KBD_KEYMAP_SIZE); + SignalSema(lineSema); +} + +void ps2kbd_ioctl_setaltmap(u8 *altmap) + +{ + //printf("ioctl_setaltmap %p\n", altmap); + WaitSema(lineSema); + memcpy(alt_map, altmap, PS2KBD_KEYMAP_SIZE); + SignalSema(lineSema); +} + +void ps2kbd_ioctl_setspecialmap(u8 *special) + +{ + //printf("ioctl_setspecialmap %p\n", special); + WaitSema(lineSema); + memcpy(special_keys, special, PS2KBD_KEYMAP_SIZE); + SignalSema(lineSema); +} + +void ps2kbd_ioctl_resetkeymap() + /* Reset keymap to default US variety */ + +{ + //printf("ioctl_resetkeymap()\n"); + WaitSema(lineSema); + memcpy(keymap, us_keymap, PS2KBD_KEYMAP_SIZE); + memcpy(shiftkeymap, us_shiftkeymap, PS2KBD_KEYMAP_SIZE); + memcpy(keycap, us_keycap, PS2KBD_KEYMAP_SIZE); + memcpy(special_keys, us_special_keys, PS2KBD_KEYMAP_SIZE); + memcpy(control_map, us_control_map, PS2KBD_KEYMAP_SIZE); + memcpy(alt_map, us_alt_map, PS2KBD_KEYMAP_SIZE); + SignalSema(lineSema); +} + +void ps2kbd_ioctl_flushbuffer() + /* Flush the internal buffer */ + +{ + //printf("ioctl_flushbuffer()\n"); + WaitSema(lineSema); + flushbuffer(); + SignalSema(lineSema); +} + +void ps2kbd_ioctl_setleds(u8 ledStatus) + +{ + int devLoop; + kbd_dev *dev; + + //printf("ioctl_setleds %d\n", ledStatus); + ledStatus &= PS2KBD_LED_MASK; + for(devLoop = 0; devLoop < PS2KBD_MAXDEV; devLoop++) + { + dev = devices[devLoop]; + if(dev) + { + if(ledStatus != dev->ledStatus) + { + dev->ledStatus = ledStatus & PS2KBD_LED_MASK; + UsbControlTransfer(dev->configEndp, 0x21, USB_REQ_SET_REPORT, 0x200, + dev->interfaceNo, 1, &dev->ledStatus, ps2kbd_led_set, dev); + } + } + } +} + +void ps2kbd_ioctl_setblockmode(u32 blockmode) + +{ + if((blockmode == PS2KBD_BLOCKING) || (blockmode == PS2KBD_NONBLOCKING)) + { + kbd_blocking = blockmode; + //printf("ioctl_setblockmode %d\n", blockmode); + } +} + +void ps2kbd_ioctl_setrepeatrate(u32 rate) + +{ + kbd_repeatrate = rate; +} + +int fio_dummy() +{ + //printf("fio_dummy()\n"); + return -5; +} + +int fio_init(iop_device_t *driver) +{ + //printf("fio_init()\n"); + return 0; +} + +int fio_format(iop_file_t *f) +{ + //printf("fio_format()\n"); + return 0; +} + +int fio_open(iop_file_t *f, const char *name, int mode) +{ + //printf("fio_open() %s %d\n", name, mode); + if(strcmp(name, PS2KBD_KBDFILE)) /* If not the keyboard file */ + { + return -1; + } + + return 0; +} + +int fio_read(iop_file_t *f, void *buf, int size) + +{ + int count = 0; + char *data = (char *) buf; + int ret; + + //printf("fio_read() %p %d\n", buf, size); + + if(kbd_readmode == PS2KBD_READMODE_RAW) + { + size &= ~1; /* Ensure size of a multiple of 2 */ + } + + ret = PollSema(bufferSema); + if(ret < 0) + { + if((ret == SEMA_ZERO) && (kbd_blocking == PS2KBD_BLOCKING)) + { + //printf("Blocking\n"); + ret = WaitSema(bufferSema); + if(ret < 0) /* Means either an error or the sema was deleted from under us */ + { + return 0; + } + } + else + { + return 0; + } + } + + SignalSema(bufferSema); + ret = WaitSema(lineSema); + if(ret < 0) return 0; + + while((count < size) && (lineStartP != lineEndP)) + { + data[count] = lineBuffer[lineStartP++]; + lineStartP %= lineSize; + count++; + PollSema(bufferSema); /* Take off one count from the sema */ + } + + SignalSema(lineSema); +/* //printf("read %d bytes\n", count); */ +/* { */ +/* struct t_sema_status s; */ + +/* ReferSemaStatus(bufferSema, &s); */ +/* //printf("Sema count %d\n", s.curr_count); */ +/* } */ + + return count; +} + +int fio_ioctl(iop_file_t *f, unsigned long arg, void *param) + +{ + //printf("fio_ioctl() %ld %d\n", arg, *((u32 *) param)); + switch(arg) + { + case PS2KBD_IOCTL_SETREADMODE: ps2kbd_ioctl_setreadmode(*((u32 *) param)); + break; + case PS2KBD_IOCTL_SETKEYMAP: ps2kbd_ioctl_setkeymap((kbd_keymap *) param); + break; + case PS2KBD_IOCTL_SETALTMAP: ps2kbd_ioctl_setaltmap((u8 *) param); + break; + case PS2KBD_IOCTL_SETCTRLMAP: ps2kbd_ioctl_setctrlmap((u8 *) param); + break; + case PS2KBD_IOCTL_SETSPECIALMAP: ps2kbd_ioctl_setspecialmap((u8 *) param); + break; + case PS2KBD_IOCTL_FLUSHBUFFER: ps2kbd_ioctl_flushbuffer(); + break; + case PS2KBD_IOCTL_SETLEDS: ps2kbd_ioctl_setleds(*(u8*) param); + break; + case PS2KBD_IOCTL_SETBLOCKMODE: ps2kbd_ioctl_setblockmode(*(u32 *) param); + break; + case PS2KBD_IOCTL_RESETKEYMAP: ps2kbd_ioctl_resetkeymap(); + break; + case PS2KBD_IOCTL_SETREPEATRATE: ps2kbd_ioctl_setrepeatrate(*(u32 *) param); + break; + default : return -1; + } + + return 0; +} + +int fio_close(iop_file_t *f) + +{ + //printf("fio_close()\n"); + return 0; +} + +iop_device_ops_t fio_ops = + + { + fio_init, + fio_dummy, + fio_format, + fio_open, + fio_close, + fio_read, + fio_dummy, + fio_dummy, + fio_ioctl, + fio_dummy, + fio_dummy, + fio_dummy, + fio_dummy, + fio_dummy, + fio_dummy, + fio_dummy, + fio_dummy + }; + +int init_fio() + +{ + kbd_filedrv.name = PS2KBD_FSNAME; + kbd_filedrv.type = IOP_DT_CHAR; + kbd_filedrv.version = 0; + kbd_filedrv.desc = "USB Keyboard FIO driver"; + kbd_filedrv.ops = &fio_ops; + return AddDrv(&kbd_filedrv); +} + +void repeat_thread(void *arg) + +{ + u32 eventmask; + int devLoop; + + for(;;) + { + WaitEventFlag(eventid, 0xFFFFFFFF, 0x01 | 0x10, &eventmask); + //printf("Recieved event %08X\n", eventmask); + for(devLoop = 0; devLoop < PS2KBD_MAXDEV; devLoop++) + { + if((eventmask & (1 << devLoop)) && (devices[devLoop])) + { + int tempPos = 0; + + WaitSema(lineSema); + if(lineStartP < lineEndP) + { + tempPos = lineStartP + lineSize; + } + else + { + tempPos = lineStartP; + } + + if((devices[devLoop]->repeatkeys[0]) && (devices[devLoop]->repeatkeys[1])) + { + if(lineEndP != (tempPos - 2)) + { + lineBuffer[lineEndP++] = devices[devLoop]->repeatkeys[0]; + lineEndP %= lineSize; + lineBuffer[lineEndP++] = devices[devLoop]->repeatkeys[1]; + lineEndP %= lineSize; + SignalSema(bufferSema); + SignalSema(bufferSema); + } + } + else if(devices[devLoop]->repeatkeys[0]) + { + if(lineEndP != (tempPos - 1)) + { + lineBuffer[lineEndP++] = devices[devLoop]->repeatkeys[0]; + lineEndP %= lineSize; + SignalSema(bufferSema); + } + } + + SignalSema(lineSema); + } + } + } +} + +int init_repeatthread() + /* Creates a thread to handle key repeats */ + +{ + iop_thread_t param; + iop_event_t event; + + event.attr = 0; + event.option = 0; + event.bits = 0; + eventid = CreateEventFlag(&event); + + param.attr = TH_C; + param.thread = repeat_thread; + param.priority = 40; + param.stacksize = 0x800; + param.option = 0; + + repeat_tid = CreateThread(¶m); + if (repeat_tid > 0) { + StartThread(repeat_tid, 0); + return 0; + } + else + { + return 1; + } +} + +int ps2kbd_init() + +{ + int ret; + iop_sema_t s; + + s.initial = 1; + s.max = 1; + s.option = 0; + s.attr = 0; + lineSema = CreateSema(&s); + if(lineSema <= 0) + { + printf("PS2KBD: Error creating sema\n"); + return 1; + } + + s.initial = 0; + s.max = PS2KBD_DEFLINELEN; + s.option = 0; + s.attr = 0; + bufferSema = CreateSema(&s); /* Create a sema to maintain status of readable data */ + if(bufferSema <= 0) + { + printf("PS2KBD: Error creating buffer sema\n"); + return 1; + } + + lineBuffer = (u8 *) AllocSysMemory(0, PS2KBD_DEFLINELEN, NULL); + if(lineBuffer == NULL) + { + printf("PS2KBD: Error allocating line buffer\n"); + return 1; + } + lineStartP = 0; + lineEndP = 0; + lineSize = PS2KBD_DEFLINELEN; + memset(lineBuffer, 0, PS2KBD_DEFLINELEN); + + memset(devices, 0, sizeof(kbd_dev *) * PS2KBD_MAXDEV); + dev_count = 0; + kbd_blocking = PS2KBD_NONBLOCKING; + kbd_readmode = PS2KBD_READMODE_NORMAL; + kbd_repeatrate = PS2KBD_DEFREPEATRATE; + memcpy(keymap, us_keymap, PS2KBD_KEYMAP_SIZE); + memcpy(shiftkeymap, us_shiftkeymap, PS2KBD_KEYMAP_SIZE); + memcpy(keycap, us_keycap, PS2KBD_KEYMAP_SIZE); + memcpy(special_keys, us_special_keys, PS2KBD_KEYMAP_SIZE); + memcpy(control_map, us_control_map, PS2KBD_KEYMAP_SIZE); + memcpy(alt_map, us_alt_map, PS2KBD_KEYMAP_SIZE); + + ret = init_fio(); + //printf("ps2kbd AddDrv [%d]\n", ret); + init_repeatthread(); + + ret = UsbRegisterDriver(&kbd_driver); + if(ret != USB_RC_OK) + { + printf("PS2KBD: Error registering USB devices\n"); + return 1; + } + + //printf("UsbRegisterDriver %d\n", ret); + + return 0; +} +#endif \ No newline at end of file diff --git a/plugins/USBqemu/qemu-usb/usb-msd.c b/plugins/USBqemu/qemu-usb/usb-msd.c new file mode 100644 index 0000000000..ff2047d4b2 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb-msd.c @@ -0,0 +1,402 @@ +/* + * USB Mass Storage Device emulation + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the LGPL. + */ + +#include "vl.h" + +//#define DEBUG_MSD + +#ifdef DEBUG_MSD +#define DPRINTF(fmt, args...) \ +do { printf("usb-msd: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + +/* USB requests. */ +#define MassStorageReset 0xff +#define GetMaxLun 0xfe + +enum USBMSDMode { + USB_MSDM_CBW, /* Command Block. */ + USB_MSDM_DATAOUT, /* Tranfer data to device. */ + USB_MSDM_DATAIN, /* Transfer data from device. */ + USB_MSDM_CSW /* Command Status. */ +}; + +typedef struct { + USBDevice dev; + enum USBMSDMode mode; + uint32_t data_len; + uint32_t tag; + SCSIDevice *scsi_dev; + int result; +} MSDState; + +static const uint8_t qemu_msd_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x00, /* u16 bcdUSB; v1.0 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + /* Vendor and product id are arbitrary. */ + 0x00, 0x00, /* u16 idVendor; */ + 0x00, 0x00, /* u16 idProduct; */ + 0x00, 0x00, /* u16 bcdDevice */ + + 0x01, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x03, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_msd_config_descriptor[] = { + + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x20, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xc0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* u8 MaxPower; */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x02, /* u8 if_bNumEndpoints; */ + 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */ + 0x06, /* u8 if_bInterfaceSubClass; SCSI */ + 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */ + 0x00, /* u8 if_iInterface; */ + + /* Bulk-In endpoint */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x00, /* u8 ep_bInterval; */ + + /* Bulk-Out endpoint */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ + 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x00 /* u8 ep_bInterval; */ +}; + +static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail) +{ + MSDState *s = (MSDState *)opaque; + + DPRINTF("Command complete\n"); + s->result = fail; + s->mode = USB_MSDM_CSW; +} + +static void usb_msd_handle_reset(USBDevice *dev) +{ + MSDState *s = (MSDState *)dev; + + DPRINTF("Reset\n"); + s->mode = USB_MSDM_CBW; +} + +static int usb_msd_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + MSDState *s = (MSDState *)dev; + int ret = 0; + + switch (request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_msd_dev_descriptor, + sizeof(qemu_msd_dev_descriptor)); + ret = sizeof(qemu_msd_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, qemu_msd_config_descriptor, + sizeof(qemu_msd_config_descriptor)); + ret = sizeof(qemu_msd_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* vendor description */ + ret = set_usb_string(data, "QEMU " QEMU_VERSION); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "QEMU USB HARDDRIVE"); + break; + case 3: + /* serial number */ + ret = set_usb_string(data, "1"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == 0 && index != 0x81) { /* clear ep halt */ + goto fail; + } + ret = 0; + break; + /* Class specific requests. */ + case MassStorageReset: + /* Reset state ready for the next CBW. */ + s->mode = USB_MSDM_CBW; + ret = 0; + break; + case GetMaxLun: + data[0] = 0; + ret = 1; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +struct usb_msd_cbw { + uint32_t sig; + uint32_t tag; + uint32_t data_len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd[16]; +}; + +struct usb_msd_csw { + uint32_t sig; + uint32_t tag; + uint32_t residue; + uint8_t status; +}; + +static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep, + uint8_t *data, int len) +{ + MSDState *s = (MSDState *)dev; + int ret = 0; + struct usb_msd_cbw cbw; + struct usb_msd_csw csw; + + switch (pid) { + case USB_TOKEN_OUT: + if (devep != 2) + goto fail; + + switch (s->mode) { + case USB_MSDM_CBW: + if (len != 31) { + fprintf(stderr, "usb-msd: Bad CBW size"); + goto fail; + } + memcpy(&cbw, data, 31); + if (le32_to_cpu(cbw.sig) != 0x43425355) { + fprintf(stderr, "usb-msd: Bad signature %08x\n", + le32_to_cpu(cbw.sig)); + goto fail; + } + DPRINTF("Command on LUN %d\n", cbw.lun); + if (cbw.lun != 0) { + fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun); + goto fail; + } + s->tag = le32_to_cpu(cbw.tag); + s->data_len = le32_to_cpu(cbw.data_len); + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } else if (cbw.flags & 0x80) { + s->mode = USB_MSDM_DATAIN; + } else { + s->mode = USB_MSDM_DATAOUT; + } + DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", + s->tag, cbw.flags, cbw.cmd_len, s->data_len); + scsi_send_command(s->scsi_dev, s->tag, cbw.cmd, 0); + ret = len; + break; + + case USB_MSDM_DATAOUT: + DPRINTF("Data out %d/%d\n", len, s->data_len); + if (len > s->data_len) + goto fail; + + if (scsi_write_data(s->scsi_dev, data, len)) + goto fail; + + s->data_len -= len; + if (s->data_len == 0) + s->mode = USB_MSDM_CSW; + ret = len; + break; + + default: + DPRINTF("Unexpected write (len %d)\n", len); + goto fail; + } + break; + + case USB_TOKEN_IN: + if (devep != 1) + goto fail; + + switch (s->mode) { + case USB_MSDM_CSW: + DPRINTF("Command status %d tag 0x%x, len %d\n", + s->result, s->tag, len); + if (len < 13) + goto fail; + + csw.sig = cpu_to_le32(0x53425355); + csw.tag = cpu_to_le32(s->tag); + csw.residue = 0; + csw.status = s->result; + memcpy(data, &csw, 13); + ret = 13; + s->mode = USB_MSDM_CBW; + break; + + case USB_MSDM_DATAIN: + DPRINTF("Data in %d/%d\n", len, s->data_len); + if (len > s->data_len) + len = s->data_len; + + if (scsi_read_data(s->scsi_dev, data, len)) + goto fail; + + s->data_len -= len; + if (s->data_len == 0) + s->mode = USB_MSDM_CSW; + ret = len; + break; + + default: + DPRINTF("Unexpected read (len %d)\n", len); + goto fail; + } + break; + + default: + DPRINTF("Bad token\n"); + fail: + ret = USB_RET_STALL; + break; + } + + return ret; +} + +static void usb_msd_handle_destroy(USBDevice *dev) +{ + MSDState *s = (MSDState *)dev; + + scsi_disk_destroy(s->scsi_dev); + qemu_free(s); +} + +USBDevice *usb_msd_init(const char *filename) +{ + MSDState *s; + BlockDriverState *bdrv; + + s = qemu_mallocz(sizeof(MSDState)); + if (!s) + return NULL; + + bdrv = bdrv_new("usb"); + bdrv_open(bdrv, filename, 0); + + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_msd_handle_reset; + s->dev.handle_control = usb_msd_handle_control; + s->dev.handle_data = usb_msd_handle_data; + s->dev.handle_destroy = usb_msd_handle_destroy; + + snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)", + filename); + + s->scsi_dev = scsi_disk_init(bdrv, usb_msd_command_complete, s); + usb_msd_handle_reset((USBDevice *)s); + return (USBDevice *)s; +} diff --git a/plugins/USBqemu/qemu-usb/usb-ohci.cpp b/plugins/USBqemu/qemu-usb/usb-ohci.cpp new file mode 100644 index 0000000000..0432149a35 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb-ohci.cpp @@ -0,0 +1,985 @@ +/* + * QEMU USB OHCI Emulation + * Copyright (c) 2004 Gianni Tedesco + * Copyright (c) 2006 CodeSourcery + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO: + * o Isochronous transfers + * o Allocate bandwidth in frames properly + * o Disable timers when nothing needs to be done, or remove timer usage + * all together. + * o Handle unrecoverable errors properly + * o BIOS work to boot from USB storage +*/ + +//typedef CPUReadMemoryFunc + +#include "vl.h" +#include "../USB.h" + +uint32_t bits = 0; +uint32_t need_interrupt = 0; + +extern FILE* usbLog; + +int dprintf(const char *fmt,...) +{ +#ifdef DEBUG_OHCI + int t; + va_list list; + + va_start(list, fmt); + t=vfprintf(stderr, fmt, list); + va_end(list); + + if(usbLog) + { + va_start(list, fmt); + vfprintf(usbLog, fmt, list); + va_end(list); + } + + return t; +#else + return 0; +#endif +} + +/* Update IRQ levels */ +static inline void ohci_intr_update(OHCIState *ohci) +{ + bits = (ohci->intr_status & ohci->intr) & 0x7fffffff; + + if ((ohci->intr & OHCI_INTR_MIE) && (bits!=0)) // && (ohci->ctl & OHCI_CTL_HCFS)) + { + /* + static char reasons[1024]; + int first=1; + + reasons[0]=0; + +#define reason_add(p,t) if(bits&(p)) { if(!first) strcat_s(reasons,1024,", "); first=0; strcat_s(reasons,1024,t); } + reason_add(OHCI_INTR_SO,"Scheduling overrun"); + reason_add(OHCI_INTR_WD,"HcDoneHead writeback"); + reason_add(OHCI_INTR_SF,"Start of frame"); + reason_add(OHCI_INTR_RD,"Resume detect"); + reason_add(OHCI_INTR_UE,"Unrecoverable error"); + reason_add(OHCI_INTR_FNO,"Frame number overflow"); + reason_add(OHCI_INTR_RHSC,"Root hub status change"); + reason_add(OHCI_INTR_OC,"Ownership change"); + */ + if((ohci->ctl & OHCI_CTL_HCFS)==OHCI_USB_OPERATIONAL) + { + USBirq(1); + //dprintf("usb-ohci: Interrupt Called. Reason(s): %s\n",reasons); + } + } +} + +/* Set an interrupt */ +static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr) +{ + ohci->intr_status |= intr; + ohci_intr_update(ohci); +} + +//static void usb_attach(USBPort *port, USBDevice *dev) +//{ +// port->attach(port,dev); +//} + +/* Attach or detach a device on a root hub port. */ +static void ohci_attach(USBPort *port1, USBDevice *dev) +{ + OHCIState *s = (OHCIState *)port1->opaque; + OHCIPort *port = (OHCIPort *)&s->rhport[port1->index]; + uint32_t old_state = port->ctrl; + + if (dev) { + if (port->port.dev) { + usb_attach(port1, NULL); + } + /* set connect status */ + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; + + /* update speed */ + if (dev->speed == USB_SPEED_LOW) + port->ctrl |= OHCI_PORT_LSDA; + else + port->ctrl &= ~OHCI_PORT_LSDA; + port->port.dev = dev; + /* send the attach message */ + dev->handle_packet(dev, + USB_MSG_ATTACH, 0, 0, NULL, 0); + dprintf("usb-ohci: Attached port %d\n", port1->index); + } else { + /* set connect status */ + if (port->ctrl & OHCI_PORT_CCS) { + port->ctrl &= ~OHCI_PORT_CCS; + port->ctrl |= OHCI_PORT_CSC; + } + /* disable port */ + if (port->ctrl & OHCI_PORT_PES) { + port->ctrl &= ~OHCI_PORT_PES; + port->ctrl |= OHCI_PORT_PESC; + } + dev = port->port.dev; + if (dev) { + /* send the detach message */ + dev->handle_packet(dev, + USB_MSG_DETACH, 0, 0, NULL, 0); + } + port->port.dev = NULL; + dprintf("usb-ohci: Detached port %d\n", port1->index); + } + + if (old_state != port->ctrl) + ohci_set_interrupt(s, OHCI_INTR_RHSC); +} + +/* Reset the controller */ +static void ohci_reset(OHCIState *ohci) +{ + OHCIPort *port; + int i; + + ohci->ctl = 0; + ohci->status = 0; + ohci->intr_status = 0; + ohci->intr = OHCI_INTR_MIE; + + + + ohci->hcca = 0; + ohci->ctrl_head = ohci->ctrl_cur = 0; + ohci->bulk_head = ohci->bulk_cur = 0; + ohci->per_cur = 0; + ohci->done = 0; + ohci->done_count = 7; + + /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? + * I took the value linux sets ... + */ + ohci->fsmps = 0x2778; + ohci->fi = 0x2edf; + ohci->fit = 0; + ohci->frt = 0; + ohci->frame_number = 0; + ohci->pstart = 0; + ohci->lst = OHCI_LS_THRESH; + + ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; + ohci->rhdesc_b = 0x0; /* Impl. specific */ + ohci->rhstatus = 0; + + for (i = 0; i < ohci->num_ports; i++) + { + port = &ohci->rhport[i]; + port->ctrl = 0; + if (port->port.dev) + ohci_attach(&port->port, port->port.dev); + } + dprintf("usb-ohci: Reset.\n"); +} + +#define le32_to_cpu(x) (x) +#define cpu_to_le32(x) (x) + +/* Get an array of dwords from main memory */ +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); + *buf = le32_to_cpu(*buf); + } + + return 1; +} + +/* Put an array of dwords in to main memory */ +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + uint32_t tmp = cpu_to_le32(*buf); + cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); + } + + return 1; +} + +static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed) +{ + return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); +} + +static inline int ohci_read_td(uint32_t addr, struct ohci_td *td) +{ + return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); +} + +static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed) +{ + return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); +} + +static inline int ohci_put_td(uint32_t addr, struct ohci_td *td) +{ + return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); +} + +/* Read/Write the contents of a TD from/to main memory. */ +static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) +{ + uint32_t ptr; + uint32_t n; + + ptr = td->cbp; + n = 0x1000 - (ptr & 0xfff); + if (n > len) + n = len; + cpu_physical_memory_rw(ptr, buf, n, write); + if (n == len) + return; + ptr = td->be & ~0xfffu; + buf += n; + cpu_physical_memory_rw(ptr, buf, len - n, write); +} + +/* Service a transport descriptor. + Returns nonzero to terminate processing of this endpoint. */ + +static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) +{ + int dir; + size_t len = 0; + uint8_t buf[8192]; + char *str = NULL; + int pid; + int ret; + int i; + USBDevice *dev; + struct ohci_td td; + uint32_t addr; + int flag_r; + + addr = ed->head & OHCI_DPTR_MASK; + if (!ohci_read_td(addr, &td)) { + fprintf(stderr, "usb-ohci: TD read error at %x\n", addr); + return 0; + } + + dir = OHCI_BM(ed->flags, ED_D); + switch (dir) { + case OHCI_TD_DIR_OUT: + case OHCI_TD_DIR_IN: + /* Same value. */ + break; + default: + dir = OHCI_BM(td.flags, TD_DP); + break; + } + + switch (dir) { + case OHCI_TD_DIR_IN: + str = "in"; + pid = USB_TOKEN_IN; + break; + case OHCI_TD_DIR_OUT: + str = "out"; + pid = USB_TOKEN_OUT; + break; + case OHCI_TD_DIR_SETUP: + str = "setup"; + pid = USB_TOKEN_SETUP; + break; + default: + fprintf(stderr, "usb-ohci: Bad direction\n"); + return 1; + } + if (td.cbp && td.be) { + if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) { + len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff); + } else { + len = (td.be - td.cbp) + 1; + } + + if (len && dir != OHCI_TD_DIR_IN) { + ohci_copy_td(&td, buf, len, 0); + } + } + + flag_r = (td.flags & OHCI_TD_R) != 0; +#ifdef DEBUG_PACKET + dprintf(" TD @ 0x%.8x %u bytes %s r=%d cbp=0x%.8x be=0x%.8x\n", + addr, len, str, flag_r, td.cbp, td.be); + + if (len >= 0 && dir != OHCI_TD_DIR_IN) { + dprintf(" data:"); + for (i = 0; i < len; i++) + printf(" %.2x", buf[i]); + dprintf("\n"); + } +#endif + ret = USB_RET_NODEV; + for (i = 0; i < ohci->num_ports; i++) { + dev = ohci->rhport[i].port.dev; + if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) + continue; + + ret = dev->handle_packet(dev, pid, OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN), buf, len); + if (ret != USB_RET_NODEV) + break; + } +#ifdef DEBUG_PACKET + dprintf("ret=%d\n", ret); +#endif + if (ret >= 0) { + if (dir == OHCI_TD_DIR_IN) { + ohci_copy_td(&td, buf, ret, 1); +#ifdef DEBUG_PACKET + dprintf(" data:"); + for (i = 0; i < ret; i++) + printf(" %.2x", buf[i]); + dprintf("\n"); +#endif + } else { + ret = len; + } + } + + /* Writeback */ + if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) { + /* Transmission succeeded. */ + if (ret == len) { + td.cbp = 0; + } else { + td.cbp += ret; + if ((td.cbp & 0xfff) + ret > 0xfff) { + td.cbp &= 0xfff; + td.cbp |= td.be & ~0xfff; + } + } + td.flags |= OHCI_TD_T1; + td.flags ^= OHCI_TD_T0; + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR); + OHCI_SET_BM(td.flags, TD_EC, 0); + + ed->head &= ~OHCI_ED_C; + if (td.flags & OHCI_TD_T0) + ed->head |= OHCI_ED_C; + } else { + if (ret >= 0) { + dprintf("usb-ohci: Underrun\n"); + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); + } else { + switch (ret) { + case USB_RET_NODEV: + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); + case USB_RET_NAK: + dprintf("usb-ohci: got NAK\n"); + return 1; + case USB_RET_STALL: + dprintf("usb-ohci: got STALL\n"); + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL); + break; + case USB_RET_BABBLE: + dprintf("usb-ohci: got BABBLE\n"); + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN); + break; + default: + fprintf(stderr, "usb-ohci: Bad device response %d\n", ret); + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID); + OHCI_SET_BM(td.flags, TD_EC, 3); + break; + } + } + ed->head |= OHCI_ED_H; + } + + /* Retire this TD */ + ed->head &= ~OHCI_DPTR_MASK; + ed->head |= td.next & OHCI_DPTR_MASK; + td.next = ohci->done; + ohci->done = addr; + i = OHCI_BM(td.flags, TD_DI); + if (i < ohci->done_count) + ohci->done_count = i; + ohci_put_td(addr, &td); + return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; +} + +/* Service an endpoint list. Returns nonzero if active TD were found. */ +static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) +{ + struct ohci_ed ed; + uint32_t next_ed; + uint32_t cur; + int active; + + active = 0; + + if (head == 0) + return 0; + + for (cur = head; cur; cur = next_ed) { + if (!ohci_read_ed(cur, &ed)) { + fprintf(stderr, "usb-ohci: ED read error at %x\n", cur); + return 0; + } + + next_ed = ed.next & OHCI_DPTR_MASK; + + if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) + continue; + + /* Skip isochronous endpoints. */ + if (ed.flags & OHCI_ED_F) + continue; + + while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { +#ifdef DEBUG_PACKET + dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " + "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur, + OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), + OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, + (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, + OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0, + (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK, + ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); +#endif + active = 1; + + if (ohci_service_td(ohci, &ed)) + break; + } + + ohci_put_ed(cur, &ed); + } + + return active; +} + +/* Generate a SOF event, and set a timer for EOF */ +static void ohci_sof(OHCIState *ohci) +{ + ohci->sof_time = get_clock(); + ohci->eof_timer = usb_frame_time; + ohci_set_interrupt(ohci, OHCI_INTR_SF); +} + +/* Do frame processing on frame boundary */ +void ohci_frame_boundary(void *opaque) +{ + OHCIState *ohci = (OHCIState *)opaque; + struct ohci_hcca hcca; + + cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0); + + /* Process all the lists at the end of the frame */ + if (ohci->ctl & OHCI_CTL_PLE) { + int n; + + n = ohci->frame_number & 0x1f; + ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n])); + } + if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { + if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) + dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur); + if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) { + ohci->ctrl_cur = 0; + ohci->status &= ~OHCI_STATUS_CLF; + } + } + + if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { + if (!ohci_service_ed_list(ohci, ohci->bulk_head)) { + ohci->bulk_cur = 0; + ohci->status &= ~OHCI_STATUS_BLF; + } + } + + /* Frame boundary, so do EOF stuf here */ + ohci->frt = ohci->fit; + + /* XXX: endianness */ + ohci->frame_number = (ohci->frame_number + 1) & 0xffff; + hcca.frame = cpu_to_le32(ohci->frame_number); + + if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { + if (!ohci->done) + abort(); + if (ohci->intr & ohci->intr_status) + ohci->done |= 1; + hcca.done = cpu_to_le32(ohci->done); + ohci->done = 0; + ohci->done_count = 7; + ohci_set_interrupt(ohci, OHCI_INTR_WD); + } + + if (ohci->done_count != 7 && ohci->done_count != 0) + ohci->done_count--; + + /* Do SOF stuff here */ + ohci_sof(ohci); + + /* Writeback HCCA */ + cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1); +} + +/* Start sending SOF tokens across the USB bus, lists are processed in + * next frame + */ +int ohci_bus_start(OHCIState *ohci) +{ + ohci->eof_timer = 0; + + dprintf("usb-ohci: USB Operational\n"); + + ohci_sof(ohci); + + return 1; +} + +/* Stop sending SOF tokens on the bus */ +void ohci_bus_stop(OHCIState *ohci) +{ + if (ohci->eof_timer) + ohci->eof_timer=0; +} + +/* Sets a flag in a port status register but only set it if the port is + * connected, if not set ConnectStatusChange flag. If flag is enabled + * return 1. + */ +static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) +{ + int ret = 1; + + /* writing a 0 has no effect */ + if (val == 0) + return 0; + + /* If CurrentConnectStatus is cleared we set + * ConnectStatusChange + */ + if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { + ohci->rhport[i].ctrl |= OHCI_PORT_CSC; + if (ohci->rhstatus & OHCI_RHS_DRWE) { + /* TODO: CSC is a wakeup event */ + } + return 0; + } + + if (ohci->rhport[i].ctrl & val) + ret = 0; + + /* set the bit */ + ohci->rhport[i].ctrl |= val; + + return ret; +} + +/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ +static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) +{ + val &= OHCI_FMI_FI; + + if (val != ohci->fi) { + dprintf("usb-ohci: FrameInterval = 0x%x (%u)\n", ohci->fi, ohci->fi); + } + + ohci->fi = val; +} + +static void ohci_port_power(OHCIState *ohci, int i, int p) +{ + if (p) { + ohci->rhport[i].ctrl |= OHCI_PORT_PPS; + } else { + ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS| + OHCI_PORT_CCS| + OHCI_PORT_PSS| + OHCI_PORT_PRS); + } +} + +/* Set HcControlRegister */ +static void ohci_set_ctl(OHCIState *ohci, uint32_t val) +{ + uint32_t old_state; + uint32_t new_state; + + old_state = ohci->ctl & OHCI_CTL_HCFS; + ohci->ctl = val; + new_state = ohci->ctl & OHCI_CTL_HCFS; + + /* no state change */ + if (old_state == new_state) + return; + + switch (new_state) { + case OHCI_USB_OPERATIONAL: + ohci_bus_start(ohci); + break; + case OHCI_USB_SUSPEND: + ohci_bus_stop(ohci); + dprintf("usb-ohci: USB Suspended\n"); + break; + case OHCI_USB_RESUME: + dprintf("usb-ohci: USB Resume\n"); + break; + case OHCI_USB_RESET: + dprintf("usb-ohci: USB Reset\n"); + break; + } + ohci_intr_update(ohci); +} + +static uint32_t ohci_get_frame_remaining(OHCIState *ohci) +{ + uint16_t fr; + int64_t tks; + + if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) + return (ohci->frt << 31); + + /* Being in USB operational state guarnatees sof_time was + * set already. + */ + tks = get_clock() - ohci->sof_time; + + /* avoid muldiv if possible */ + if (tks >= usb_frame_time) + return (ohci->frt << 31); + + tks = muldiv64(1, tks, usb_bit_time); + fr = (uint16_t)(ohci->fi - tks); + + return (ohci->frt << 31) | fr; +} + + +/* Set root hub status */ +static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) +{ + uint32_t old_state; + + old_state = ohci->rhstatus; + + /* write 1 to clear OCIC */ + if (val & OHCI_RHS_OCIC) + ohci->rhstatus &= ~OHCI_RHS_OCIC; + + if (val & OHCI_RHS_LPS) { + int i; + + for (i = 0; i < ohci->num_ports; i++) + ohci_port_power(ohci, i, 0); + dprintf("usb-ohci: powered down all ports\n"); + } + + if (val & OHCI_RHS_LPSC) { + int i; + + for (i = 0; i < ohci->num_ports; i++) + ohci_port_power(ohci, i, 1); + dprintf("usb-ohci: powered up all ports\n"); + } + + if (val & OHCI_RHS_DRWE) + ohci->rhstatus |= OHCI_RHS_DRWE; + + if (val & OHCI_RHS_CRWE) + ohci->rhstatus &= ~OHCI_RHS_DRWE; + + if (old_state != ohci->rhstatus) + ohci_set_interrupt(ohci, OHCI_INTR_RHSC); +} + +/* Set root hub port status */ +static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) +{ + uint32_t old_state; + OHCIPort *port; + + port = &ohci->rhport[portnum]; + old_state = port->ctrl; + + /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ + if (val & OHCI_PORT_WTC) + port->ctrl &= ~(val & OHCI_PORT_WTC); + + if (val & OHCI_PORT_CCS) + port->ctrl &= ~OHCI_PORT_PES; + + ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); + + if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) + dprintf("usb-ohci: port %d: SUSPEND\n", portnum); + + if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { + dprintf("usb-ohci: port %d: RESET\n", portnum); + port->port.dev->handle_packet(port->port.dev, USB_MSG_RESET, + 0, 0, NULL, 0); + port->ctrl &= ~OHCI_PORT_PRS; + /* ??? Should this also set OHCI_PORT_PESC. */ + port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; + } + + /* Invert order here to ensure in ambiguous case, device is + * powered up... + */ + if (val & OHCI_PORT_LSDA) + ohci_port_power(ohci, portnum, 0); + if (val & OHCI_PORT_PPS) + ohci_port_power(ohci, portnum, 1); + + if (old_state != port->ctrl) + ohci_set_interrupt(ohci, OHCI_INTR_RHSC); + + return; +} + +uint32_t ohci_mem_read(OHCIState *ptr, uint32_t addr) +{ + OHCIState *ohci = ptr; + + addr -= ohci->mem_base; + + /* Only aligned reads are allowed on OHCI */ + if (addr & 3) { + fprintf(stderr, "usb-ohci: Mis-aligned read\n"); + return 0xffffffff; + } + + if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { + /* HcRhPortStatus */ + return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; + } + + switch (addr >> 2) { + case 0: /* HcRevision */ + return 0x10; + + case 1: /* HcControl */ + return ohci->ctl; + + case 2: /* HcCommandStatus */ + return ohci->status; + + case 3: /* HcInterruptStatus */ + return ohci->intr_status; + + case 4: /* HcInterruptEnable */ + case 5: /* HcInterruptDisable */ + return ohci->intr; + + case 6: /* HcHCCA */ + return ohci->hcca; + + case 7: /* HcPeriodCurrentED */ + return ohci->per_cur; + + case 8: /* HcControlHeadED */ + return ohci->ctrl_head; + + case 9: /* HcControlCurrentED */ + return ohci->ctrl_cur; + + case 10: /* HcBulkHeadED */ + return ohci->bulk_head; + + case 11: /* HcBulkCurrentED */ + return ohci->bulk_cur; + + case 12: /* HcDoneHead */ + return ohci->done; + + case 13: /* HcFmInterval */ + return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); + + case 14: /* HcFmRemaining */ + return ohci_get_frame_remaining(ohci); + + case 15: /* HcFmNumber */ + return ohci->frame_number; + + case 16: /* HcPeriodicStart */ + return ohci->pstart; + + case 17: /* HcLSThreshold */ + return ohci->lst; + + case 18: /* HcRhDescriptorA */ + return ohci->rhdesc_a; + + case 19: /* HcRhDescriptorB */ + return ohci->rhdesc_b; + + case 20: /* HcRhStatus */ + return ohci->rhstatus; + + default: + fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr); + return 0xffffffff; + } +} + +void ohci_mem_write(OHCIState *ptr,uint32_t addr, uint32_t val) +{ + OHCIState *ohci = ptr; + + addr -= ohci->mem_base; + + /* Only aligned reads are allowed on OHCI */ + if (addr & 3) { + fprintf(stderr, "usb-ohci: Mis-aligned write\n"); + return; + } + + if ((addr >= 0x54) && (addr < (0x54 + ohci->num_ports * 4))) { + /* HcRhPortStatus */ + ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); + return; + } + + switch (addr >> 2) { + case 1: /* HcControl */ + ohci_set_ctl(ohci, val); + break; + + case 2: /* HcCommandStatus */ + /* SOC is read-only */ + val = (val & ~OHCI_STATUS_SOC); + + /* Bits written as '0' remain unchanged in the register */ + ohci->status |= val; + + if (ohci->status & OHCI_STATUS_HCR) + ohci_reset(ohci); + break; + + case 3: /* HcInterruptStatus */ + ohci->intr_status &= ~val; + ohci_intr_update(ohci); + break; + + case 4: /* HcInterruptEnable */ + ohci->intr |= val; + ohci_intr_update(ohci); + break; + + case 5: /* HcInterruptDisable */ + ohci->intr &= ~val; + ohci_intr_update(ohci); + break; + + case 6: /* HcHCCA */ + ohci->hcca = val & OHCI_HCCA_MASK; + break; + + case 8: /* HcControlHeadED */ + ohci->ctrl_head = val & OHCI_EDPTR_MASK; + break; + + case 9: /* HcControlCurrentED */ + ohci->ctrl_cur = val & OHCI_EDPTR_MASK; + break; + + case 10: /* HcBulkHeadED */ + ohci->bulk_head = val & OHCI_EDPTR_MASK; + break; + + case 11: /* HcBulkCurrentED */ + ohci->bulk_cur = val & OHCI_EDPTR_MASK; + break; + + case 13: /* HcFmInterval */ + ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16; + ohci->fit = (val & OHCI_FMI_FIT) >> 31; + ohci_set_frame_interval(ohci, val); + break; + + case 16: /* HcPeriodicStart */ + ohci->pstart = val & 0xffff; + break; + + case 17: /* HcLSThreshold */ + ohci->lst = val & 0xffff; + break; + + case 18: /* HcRhDescriptorA */ + ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK; + ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK; + break; + + case 19: /* HcRhDescriptorB */ + break; + + case 20: /* HcRhStatus */ + ohci_set_hub_status(ohci, val); + break; + + default: + fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr); + break; + } +} + +OHCIState *ohci_create(uint32_t base, int ports) +{ + OHCIState *ohci=(OHCIState*)malloc(sizeof(OHCIState)); + int i; + + const int ticks_per_sec = PSXCLK; + + memset(ohci,0,sizeof(OHCIState)); + + ohci->mem_base=base; + + if (usb_frame_time == 0) { +#if OHCI_TIME_WARP + usb_frame_time = ticks_per_sec; + usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000); +#else + usb_frame_time = muldiv64(1, ticks_per_sec, 1000); + if (ticks_per_sec >= USB_HZ) { + usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ); + } else { + usb_bit_time = 1; + } +#endif + dprintf("usb-ohci: usb_bit_time=%lli usb_frame_time=%lli\n", + usb_frame_time, usb_bit_time); + } + + ohci->num_ports = ports; + for (i = 0; i < ports; i++) { + ohci->rhport[i].port.opaque = ohci; + ohci->rhport[i].port.index = i; + ohci->rhport[i].port.attach = ohci_attach; + } + + ohci_reset(ohci); + + return ohci; +} diff --git a/plugins/USBqemu/qemu-usb/usb.h b/plugins/USBqemu/qemu-usb/usb.h new file mode 100644 index 0000000000..bfbd5d3824 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/usb.h @@ -0,0 +1,175 @@ +/* + * QEMU USB API + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define USB_TOKEN_SETUP 0x2d +#define USB_TOKEN_IN 0x69 /* device -> host */ +#define USB_TOKEN_OUT 0xe1 /* host -> device */ + +/* specific usb messages, also sent in the 'pid' parameter */ +#define USB_MSG_ATTACH 0x100 +#define USB_MSG_DETACH 0x101 +#define USB_MSG_RESET 0x102 + +#define USB_RET_NODEV (-1) +#define USB_RET_NAK (-2) +#define USB_RET_STALL (-3) +#define USB_RET_BABBLE (-4) + +#define USB_SPEED_LOW 0 +#define USB_SPEED_FULL 1 +#define USB_SPEED_HIGH 2 + +#define USB_STATE_NOTATTACHED 0 +#define USB_STATE_ATTACHED 1 +//#define USB_STATE_POWERED 2 +#define USB_STATE_DEFAULT 3 +//#define USB_STATE_ADDRESS 4 +//#define USB_STATE_CONFIGURED 5 +#define USB_STATE_SUSPENDED 6 + +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b +#define USB_CLASS_CONTENT_SEC 0x0d +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define InterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define InterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_DEVICE_SELF_POWERED 0 +#define USB_DEVICE_REMOTE_WAKEUP 1 + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_CLASS 0x24 + +typedef struct USBPort USBPort; +typedef struct USBDevice USBDevice; + +/* definition of a USB device */ +struct USBDevice { + void *opaque; + int (*handle_packet)(USBDevice *dev, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len); + void (*handle_destroy)(USBDevice *dev); + + int speed; + + /* The following fields are used by the generic USB device + layer. They are here just to avoid creating a new structure for + them. */ + void (*handle_reset)(USBDevice *dev); + int (*handle_control)(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data); + int (*handle_data)(USBDevice *dev, int pid, uint8_t devep, + uint8_t *data, int len); + uint8_t addr; + char devname[32]; + + int state; + uint8_t setup_buf[8]; + uint8_t data_buf[1024]; + int remote_wakeup; + int setup_state; + int setup_len; + int setup_index; +}; + +typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev); + +/* USB port on which a device can be connected */ +struct USBPort { + USBDevice *dev; + usb_attachfn attach; + void *opaque; + int index; /* internal port index, may be used with the opaque */ + struct USBPort *next; /* Used internally by qemu. */ +}; + +void usb_attach(USBPort *port, USBDevice *dev); +int usb_generic_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len); +int set_usb_string(uint8_t *buf, const char *str); + +/* usb hub */ +USBDevice *usb_hub_init(int nb_ports); + +/* usb-ohci.c */ +void usb_ohci_init(void *bus, int num_ports, int devfn); + +/* usb-hid.c */ +USBDevice *usb_mouse_init(void); + +/* usb-kbd.c */ +USBDevice *usb_keyboard_init(void); + +/* usb-msd.c */ +USBDevice *usb_msd_init(const char *filename); diff --git a/plugins/USBqemu/qemu-usb/vl.cpp b/plugins/USBqemu/qemu-usb/vl.cpp new file mode 100644 index 0000000000..965fd6ebdb --- /dev/null +++ b/plugins/USBqemu/qemu-usb/vl.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include "vl.h" + +void cpu_physical_memory_rw(uint32_t addr, uint8_t *buf, + int len, int is_write); + +static inline void cpu_physical_memory_read(uint32_t addr, + uint8_t *buf, int len) +{ + cpu_physical_memory_rw(addr, buf, len, 0); +} +static inline void cpu_physical_memory_write(uint32_t addr, + const uint8_t *buf, int len) +{ + cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1); +} + +/* compute with 96 bit intermediate result: (a*b)/c */ +uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + union { + uint64_t ll; + struct { +#ifdef WORDS_BIGENDIAN + uint32_t high, low; +#else + uint32_t low, high; +#endif + } l; + } u, res; + uint64_t rl, rh; + + u.ll = a; + rl = (uint64_t)u.l.low * (uint64_t)b; + rh = (uint64_t)u.l.high * (uint64_t)b; + rh += (rl >> 32); + res.l.high = rh / c; + res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; + return res.ll; +} diff --git a/plugins/USBqemu/qemu-usb/vl.h b/plugins/USBqemu/qemu-usb/vl.h new file mode 100644 index 0000000000..a4b0a51e30 --- /dev/null +++ b/plugins/USBqemu/qemu-usb/vl.h @@ -0,0 +1,102 @@ +/* + * QEMU System Emulator header + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef VL_H +#define VL_H + +/* we put basic includes here to avoid repeating them in device drivers */ +#include +#include +#include +#include + +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#include +#include +#include +#include +#include +#include + +#define inline __inline + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef _WIN32 +#include +#define fsync _commit +#define lseek _lseeki64 +#define ENOTSUP 4096 +extern int qemu_ftruncate64(int, int64_t); +#define ftruncate qemu_ftruncate64 + + +static inline char *realpath(const char *path, char *resolved_path) +{ + _fullpath(resolved_path, path, _MAX_PATH); + return resolved_path; +} + +#define PRId64 "I64d" +#define PRIx64 "I64x" +#define PRIu64 "I64u" +#define PRIo64 "I64o" +#endif + +#include "usb.h" + +#ifndef glue +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/* vl.c */ +uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); +void cpu_physical_memory_rw(uint32_t addr, uint8_t *buf, int len, int is_write); +static inline void cpu_physical_memory_read(uint32_t addr, uint8_t *buf, int len); +static inline void cpu_physical_memory_write(uint32_t addr, const uint8_t *buf, int len); +void *qemu_mallocz(uint32_t size); + +#endif /* VL_H */ diff --git a/plugins/USBqemu/usb-eyetoy/usb-eyetoy.cpp b/plugins/USBqemu/usb-eyetoy/usb-eyetoy.cpp new file mode 100644 index 0000000000..8910deb070 --- /dev/null +++ b/plugins/USBqemu/usb-eyetoy/usb-eyetoy.cpp @@ -0,0 +1,7873 @@ +/* + * QEMU USB HUB emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +//#define DEBUG + +#define MAX_PORTS 8 + +#include "../qemu-usb/vl.h" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +typedef struct EYETOYState { + USBDevice dev; + //nothing yet +} EYETOYState; + +/* same as Linux kernel root hubs */ + +/* mostly the same values as the Bochs USB Mouse device */ +static const uint8_t eyetoy_dev_descriptor[] = { + 0x12, /* bLength */ + 0x01, /* bDescriptorType */ + 0x10, 0x01, /* bcdUSB */ + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + 0x08, /* bMaxPacketSize0 */ + 0x4c, 0x05, /* idVendor */ + 0x55, 0x01, /* idProduct */ + 0x00, 0x01, /* bcdDevice */ + 0x01, /* iManufacturer */ + 0x02, /* iProduct */ + 0x00, /* iSerialNumber */ + 0x01, /* bNumConfigurations */ +}; + +/* XXX: patch interrupt size */ +static const uint8_t eyetoy_config_descriptor[] = { + + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0xb4, 0x00, /* u16 wTotalLength; */ + 0x03, /* u8 bNumInterfaces; (3) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0x90, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0xfa, /* u8 MaxPower; */ + + /* interface #0 alternate setting #0 */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0xff, /* u8 if_bInterfaceClass; Vendor Specific */ + 0x00, /* u8 if_bInterfaceSubClass; */ + 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* u8 if_iInterface; */ + + /* interface #0 alternate setting #0 endpoint */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x01, /* u8 ep_bmAttributes; */ + 0x00, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0x01, /* u8 ep_bInterval; */ + + /* interface #0 alternate setting #1 */ + 0x09, 0x04, 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x01, 0x80, 0x01, 0x01, + + /* interface #0 alternate setting #2*/ + 0x09, 0x04, 0x00, 0x02, 0x01, 0xff, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x01, 0x00, 0x02, 0x01, + + /* interface #0 alternate setting #3 */ + 0x09, 0x04, 0x00, 0x03, 0x01, 0xff, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x01, 0x00, 0x03, 0x01, + + /* interface #0 alternate setting #4 */ + 0x09, 0x04, 0x00, 0x04, 0x01, 0xff, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x01, 0x80, 0x03, 0x01, + + /* interface #1 alternate setting #0 */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x01, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x00, /* u8 if_bNumEndpoints; */ + 0x01, /* u8 if_bInterfaceClass; AUDIO */ + 0x01, /* u8 if_bInterfaceSubClass; AUDIOCONTROL*/ + 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* u8 if_iInterface; */ + + /* interface #1 alternate setting #0 classes */ + 0x09, 0x24, 0x01, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x02, + 0x0c, 0x24, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x24, 0x03, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, + + /* interface #2 alternate setting #0 (AUDIO class, AUDIOSTREAMING subclass) */ + 0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + + /* interface #2 alternate setting #1 (AUDIO class, AUDIOSTREAMING subclass) */ + 0x09, 0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, + + /* interface #1 class-specific interface (HEADER) */ + 0x07, 0x24, 0x01, 0x02, 0x01, 0x01, 0x00, + + /* interface #1 class-specific interface (INPUT TERMINAL) */ + 0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01, 0x80, 0x3e, 0x00, + + /* interface #1 alternate setting #1 endpoint */ + 0x09, 0x05, 0x82, 0x05, 0x28, 0x00, 0x01, 0x00, 0x00, + + /* interface #1 class-specific endpoint */ + 0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +static void eyetoy_handle_reset(USBDevice *dev) +{ + /* XXX: do it */ + return; +} + +static int eyetoy_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + EYETOYState *s = (EYETOYState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, eyetoy_dev_descriptor, + sizeof(eyetoy_dev_descriptor)); + ret = sizeof(eyetoy_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, eyetoy_config_descriptor, + sizeof(eyetoy_config_descriptor)); + ret = sizeof(eyetoy_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "3X0420811"); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "EyeToy USB camera Namtai"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "PCSX2/QEMU"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + //switch(value >> 8) { + //((case 0x22: + // memcpy(data, qemu_mouse_hid_report_descriptor, + // sizeof(qemu_mouse_hid_report_descriptor)); + // ret = sizeof(qemu_mouse_hid_report_descriptor); + // break; + //default: + goto fail; + //} + break; + case GET_REPORT: + ret = 0; + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int eyetoy_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + EYETOYState *s = (EYETOYState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + + +static void eyetoy_handle_destroy(USBDevice *dev) +{ + EYETOYState *s = (EYETOYState *)dev; + + free(s); +} + +int eyetoy_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + fprintf(stderr,"usb-eyetoy: packet received with pid=%x, devaddr=%x, devep=%x and len=%x\n",pid,devaddr,devep,len); + usb_generic_handle_packet(s,pid,devaddr,devep,data,len); +} + +USBDevice *eyetoy_init() +{ + EYETOYState *s; + + s = qemu_mallocz(sizeof(EYETOYState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = eyetoy_handle_packet; + + s->dev.handle_reset = eyetoy_handle_reset; + s->dev.handle_control = eyetoy_handle_control; + s->dev.handle_data = eyetoy_handle_data; + s->dev.handle_destroy = eyetoy_handle_destroy; + + strncpy(s->dev.devname, "EyeToy USB camera Namtai", sizeof(s->dev.devname)); + + return (USBDevice *)s; + +} + +#ifdef X_DRIVER +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +/* Video-to-USB Bridge Driver for OmniVision OV511/OV511+/OV518/OV518+/OV519 + * + * Copyright (c) 1999-2005 Mark W. McClelland + * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * + * Original decompression code Copyright 1998-2000 OmniVision Technologies + * Many improvements by Bret Wallach + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * Snapshot code by Kevin Moore + * OV7620 fixes by Charl P. Botha + * Changes by Claudio Matsuoka + * Original SAA7111A code by Dave Perks + * URB error messages from pwc driver by Nemosoft + * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox + * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others + * OV7x3x/7x4x detection by Franz Reinhardt + * 2004/01/25: Added OV7640 and EyeToy support (Mark McClelland) + * + * Based on the Linux CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt. + * + * Please see the file: doc/ov51x.txt + * and the website at: http://alpha.dyndns.org/ov511 + * for more info. + * For Questions on OV519 or OV8610 please contact + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef OV511_ALLOW_CONVERSION + /* Pixel count * 3 bytes for RGB */ + #define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3) +#else + /* Pixel count * bytes per YUV420 pixel (1.5) */ + #define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2) +#endif + +#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval)) + +/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */ +#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024) + +#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) + +/* URB error codes: */ +static struct symbolic_list urb_errlist[] = { + { -ENOSR, "Buffer error (overrun)" }, + { -EPIPE, "Stalled (device not responding)" }, + { -EOVERFLOW, "Babble (bad cable?)" }, + { -EPROTO, "Bit-stuff error (bad cable?)" }, + { -EILSEQ, "CRC/Timeout" }, + { -ETIMEDOUT, "NAK (device does not respond)" }, + { -1, NULL } +}; + + +/********************************************************************** + * /proc interface + * Based on the CPiA driver version 0.7.4 -claudio + **********************************************************************/ + +#if defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry *ov511_proc_entry = NULL; +extern struct proc_dir_entry *video_proc_entry; + +/* Prototypes */ +static void ov51x_clear_snapshot(struct usb_ov511 *); +static int sensor_get_picture(struct usb_ov511 *, struct video_picture *); +static int sensor_get_exposure(struct usb_ov511 *, unsigned char *); +static int ov51x_check_snapshot(struct usb_ov511 *); +static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); + +static struct file_operations ov511_control_fops = { + .ioctl = ov51x_control_ioctl, +}; + +#define YES_NO(x) ((x) ? "yes" : "no") + + +/********************************************************************** + * + * Register I/O + * + **********************************************************************/ + +/* Write an OV51x register + + +ov->cbuf[0] = value; + +usb_control_msg: +pipe: ((PIPE_CONTROL << 30) | (dev->devnum << 8) | (0 << 15)), +request: 1, +requesttype: USB_TYPE_VENDOR | USB_RECIP_DEVICE, +value: 0, +index: (__u16)reg, +bytes: &ov->cbuf[0], +size: 1, +timeout: 1000 + +*/ + +/* Read from an OV51x register + +usb_control_msg: +pipe: ((PIPE_CONTROL << 30) | (dev->devnum << 8) | (0 << 15)), +request: 1, +requesttype: USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +value: 0, +index: (__u16)reg, +bytes: &ov->cbuf[0], +size: 1, +timeout: 1000 + +if result<0 then error +else value = ov->cbuf[0]; + +*/ + +/* + * Writes a multbyte value to a single register. Only valid with certain + * registers (0x30 and 0xc4 - 0xce). + + ov->cbuf = value (in le order) + + rc = usb_control_msg(ov->dev, + usb_sndctrlpipe(ov->dev, 0) + 1 // REG_IO + USB_TYPE_VENDOR | USB_RECIP_DEVICE + 0 + (__u16)reg + ov->cbuf + n + 1000 + + */ + +static int +ov511_upload_quan_tables(struct usb_ov511 *ov) +{ + unsigned char *pYTable = yQuanTable511; + unsigned char *pUVTable = uvQuanTable511; + unsigned char val0, val1; + int i, rc, reg = R511_COMP_LUT_BEGIN; + + PDEBUG(4, "Uploading quantization tables"); + + for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) { + if (ENABLE_Y_QUANTABLE) { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg, val0); + if (rc < 0) + return rc; + } + + if (ENABLE_UV_QUANTABLE) { + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0); + if (rc < 0) + return rc; + } + + reg++; + } + + return 0; +} + +/* OV518 quantization tables are 8x4 (instead of 8x8) */ +static int +ov518_upload_quan_tables(struct usb_ov511 *ov) +{ + unsigned char *pYTable = yQuanTable518; + unsigned char *pUVTable = uvQuanTable518; + unsigned char val0, val1; + int i, rc, reg = R511_COMP_LUT_BEGIN; + + PDEBUG(4, "Uploading quantization tables"); + + for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) { + if (ENABLE_Y_QUANTABLE) { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg, val0); + if (rc < 0) + return rc; + } + + if (ENABLE_UV_QUANTABLE) { + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0); + if (rc < 0) + return rc; + } + + reg++; + } + + return 0; +} + +static int +ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) +{ + int rc = -1; + + if (ov->bclass == BCL_OV519) { + //~~~ + PDEBUG(1, "Reset: type=0x0f"); + switch (reset_type) { + case OV511_RESET_NOREGS: + rc = reg_w(ov, OV519_SYS_RESET1, 0x0f); +// rc = reg_w(ov, OV519_SYS_RESET0, 0xdc); +// rc = reg_w(ov, OV519_SYS_RESET0, 0); + rc = reg_w(ov, OV519_SYS_RESET1, 0); + break; + } + } else { + /* Setting bit 0 not allowed on 518/518Plus */ + if (ov->bclass == BCL_OV518) + reset_type &= 0xfe; + + PDEBUG(4, "Reset: type=0x%02X", reset_type); + + rc = reg_w(ov, R51x_SYS_RESET, reset_type); + rc = reg_w(ov, R51x_SYS_RESET, 0); + } + if (rc < 0) + err("reset: command failed"); + + return rc; +} + +/********************************************************************** + * + * Low-level I2C I/O functions + * + **********************************************************************/ + +/* NOTE: Do not call this function directly! + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_w(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int +ov518_i2c_write_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value) +{ + int rc; + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_3, reg); + if (rc < 0) return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(ov, R51x_I2C_DATA, value); + if (rc < 0) return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(ov, R518_I2C_CTL, 0x01); + if (rc < 0) return rc; + + return 0; +} + +/* NOTE: Do not call this function directly! */ +static int +ov511_i2c_write_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value) +{ + int rc, retries; + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* Three byte write cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_3, reg); + if (rc < 0) return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(ov, R51x_I2C_DATA, value); + if (rc < 0) return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(ov, R511_I2C_CTL, 0x01); + if (rc < 0) return rc; + + do rc = reg_r(ov, R511_I2C_CTL); + while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ + if (rc < 0) return rc; + + if ((rc&2) == 0) /* Ack? */ + break; +#if 0 + /* I2C abort */ + reg_w(ov, R511_I2C_CTL, 0x10); +#endif + if (--retries < 0) { + PDEBUG(5, "i2c write retries exhausted"); + return -1; + } + } + + return 0; +} + +/* NOTE: Do not call this function directly! + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_r(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int +ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) +{ + int rc, value; + + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_2, reg); + if (rc < 0) return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(ov, R518_I2C_CTL, 0x03); + if (rc < 0) return rc; + + /* Initiate 2-byte read cycle */ + rc = reg_w(ov, R518_I2C_CTL, 0x05); + if (rc < 0) return rc; + + value = reg_r(ov, R51x_I2C_DATA); + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + return value; +} + +/* NOTE: Do not call this function directly! + * returns: negative is error, pos or zero is data */ +static int +ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_2, reg); + if (rc < 0) return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(ov, R511_I2C_CTL, 0x03); + if (rc < 0) return rc; + + do rc = reg_r(ov, R511_I2C_CTL); + while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ + if (rc < 0) return rc; + + if ((rc&2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(ov, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + PDEBUG(5, "i2c write retries exhausted"); + return -1; + } + } + + /* Two byte read cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Initiate 2-byte read cycle */ + rc = reg_w(ov, R511_I2C_CTL, 0x05); + if (rc < 0) return rc; + + do rc = reg_r(ov, R511_I2C_CTL); + while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ + if (rc < 0) return rc; + + if ((rc&2) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = reg_w(ov, R511_I2C_CTL, 0x10); + if (rc < 0) return rc; + + if (--retries < 0) { + PDEBUG(5, "i2c read retries exhausted"); + return -1; + } + } + + value = reg_r(ov, R51x_I2C_DATA); + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* This is needed to make i2c_w() work */ + rc = reg_w(ov, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + return value; +} + +/* returns: negative is error, pos or zero is data */ +static int +i2c_r(struct usb_ov511 *ov, unsigned char reg) +{ + int rc; + + down(&ov->i2c_lock); + + switch (ov->bclass) { + case BCL_OV511: + rc = ov511_i2c_read_internal(ov, reg); + break; + case BCL_OV518: + case BCL_OV519: + rc = ov518_i2c_read_internal(ov, reg); + break; + default: + err("i2c_r: Invalid bridge class"); + rc = -EINVAL; + } + up(&ov->i2c_lock); + + return rc; +} + +static int +i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) +{ + int rc; + + down(&ov->i2c_lock); + + switch (ov->bclass) { + case BCL_OV511: + rc = ov511_i2c_write_internal(ov, reg, value); + break; + case BCL_OV518: + case BCL_OV519: + rc = ov518_i2c_write_internal(ov, reg, value); + break; + default: + err("ic2_w: Invalid bridge class"); + rc = -EINVAL; + } + + up(&ov->i2c_lock); + + return rc; +} + +/* Do not call this function directly! */ +static int +ov51x_i2c_write_mask_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + unsigned char oldval, newval; + + if (mask == 0xff) { + newval = value; + } else { + switch (ov->bclass) { + case BCL_OV511: + rc = ov511_i2c_read_internal(ov, reg); + break; + case BCL_OV518: + case BCL_OV519: + rc = ov518_i2c_read_internal(ov, reg); + break; + default: + err("ov51x_i2c_write_mask_internal: Invalid bridge class"); + rc = -EINVAL; + } + if (rc < 0) + return rc; + + oldval = (unsigned char) rc; + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + } + + switch (ov->bclass) { + case BCL_OV511: + return (ov511_i2c_write_internal(ov, reg, newval)); + break; + case BCL_OV518: + case BCL_OV519: + return (ov518_i2c_write_internal(ov, reg, newval)); + break; + default: + return -EINVAL; + } +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int +i2c_w_mask(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + + down(&ov->i2c_lock); + rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); + up(&ov->i2c_lock); + + return rc; +} + +/* Do not call this function directly! */ +static int +ov51x_i2c_setbit_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char bitaddr) +{ + int rc; + unsigned char newval; + + switch (ov->bclass) { + case BCL_OV511: + rc = ov511_i2c_read_internal(ov, reg); + break; + case BCL_OV518: + case BCL_OV519: + rc = ov518_i2c_read_internal(ov, reg); + break; + default: + err("ov51x_i2c_setbit_internal: Invalid bridge class"); + rc = -EINVAL; + } + if (rc < 0) + return rc; + + newval = ((unsigned char)rc & ~(1 << bitaddr)) | (value ? (1 << bitaddr) : 0); /* Set the desired bit */ + + switch (ov->bclass) { + case BCL_OV511: + return (ov511_i2c_write_internal(ov, reg, newval)); + break; + case BCL_OV518: + case BCL_OV519: + return (ov518_i2c_write_internal(ov, reg, newval)); + break; + default: + return -EINVAL; + } +} + +/* Writes bits at positions specified by bitaddr to an I2C reg. Bits are cleared + * if value = 0 and set if value = 1. + */ +static int +i2c_setbit(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char bitaddr) +{ + int rc; + + down(&ov->i2c_lock); + rc = ov51x_i2c_setbit_internal(ov, reg, value, bitaddr); + up(&ov->i2c_lock); + + return rc; +} + +/* Set the read and write slave IDs. The "slave" argument is the write slave, + * and the read slave will be set to (slave + 1). ov->i2c_lock should be held + * when calling this. This should not be called from outside the i2c I/O + * functions. + */ +static int +i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave) +{ + int rc; + + rc = reg_w(ov, R51x_I2C_W_SID, slave); + if (rc < 0) return rc; + + rc = reg_w(ov, R51x_I2C_R_SID, slave + 1); + if (rc < 0) return rc; + + return 0; +} + +/* Write to a specific I2C slave ID and register, using the specified mask */ +static int +i2c_w_slave(struct usb_ov511 *ov, + unsigned char slave, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc = 0; + + down(&ov->i2c_lock); + + /* Set new slave IDs */ + rc = i2c_set_slave_internal(ov, slave); + if (rc < 0) goto out; + + rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); + +out: + /* Restore primary IDs */ + if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) + err("Couldn't restore primary I2C slave"); + + up(&ov->i2c_lock); + return rc; +} + +/* Read from a specific I2C slave ID and register */ +static int +i2c_r_slave(struct usb_ov511 *ov, + unsigned char slave, + unsigned char reg) +{ + int rc; + + down(&ov->i2c_lock); + + /* Set new slave IDs */ + rc = i2c_set_slave_internal(ov, slave); + if (rc < 0) goto out; + + switch (ov->bclass) { + case BCL_OV511: + rc = ov511_i2c_read_internal(ov, reg); + break; + case BCL_OV518: + case BCL_OV519: + rc = ov518_i2c_read_internal(ov, reg); + break; + default: + err("i2c_r_slave: Invalid bridge class"); + rc = -EINVAL; + } +out: + /* Restore primary IDs */ + if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) + err("Couldn't restore primary I2C slave"); + + up(&ov->i2c_lock); + return rc; +} + +/* Sets I2C read and write slave IDs. Returns <0 for error */ +static int +ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) +{ + int rc; + + down(&ov->i2c_lock); + + rc = i2c_set_slave_internal(ov, sid); + if (rc < 0) goto out; + + // FIXME: Is this actually necessary? + if (ov->bclass != BCL_OV519) + rc = ov51x_reset(ov, OV511_RESET_NOREGS); + if (rc < 0) goto out; + +out: + up(&ov->i2c_lock); + return rc; +} + +static int +write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals) +{ + int rc; + + while (pRegvals->bus != OV511_DONE_BUS) { + if (pRegvals->bus == OV511_REG_BUS) { + if ((rc = reg_w(ov, pRegvals->reg, pRegvals->val)) < 0) + return rc; + } else if (pRegvals->bus == OV511_I2C_BUS) { + if ((rc = i2c_w(ov, pRegvals->reg, pRegvals->val)) < 0) + return rc; + } else { + err("Bad regval array"); + return -1; + } + pRegvals++; + } + return 0; +} + +#ifdef OV511_DEBUG +static void +dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) +{ + int i, rc; + + for (i = reg1; i <= regn; i++) { + rc = i2c_r(ov, i); + info("Sensor[0x%02X] = 0x%02X", i, rc); + } +} + +static void +dump_i2c_regs(struct usb_ov511 *ov) +{ + info("I2C REGS"); + dump_i2c_range(ov, 0x00, 0x7C); +} + +static void +dump_reg_range(struct usb_ov511 *ov, int reg1, int regn) +{ + int i, rc; + + for (i = reg1; i <= regn; i++) { + rc = reg_r(ov, i); + info("OV511[0x%02X] = 0x%02X", i, rc); + } +} + +static void +ov511_dump_regs(struct usb_ov511 *ov) +{ + info("CAMERA INTERFACE REGS"); + dump_reg_range(ov, 0x10, 0x1f); + info("DRAM INTERFACE REGS"); + dump_reg_range(ov, 0x20, 0x23); + info("ISO FIFO REGS"); + dump_reg_range(ov, 0x30, 0x31); + info("PIO REGS"); + dump_reg_range(ov, 0x38, 0x39); + dump_reg_range(ov, 0x3e, 0x3e); + info("I2C REGS"); + dump_reg_range(ov, 0x40, 0x49); + info("SYSTEM CONTROL REGS"); + dump_reg_range(ov, 0x50, 0x55); + dump_reg_range(ov, 0x5e, 0x5f); + info("OmniCE REGS"); + dump_reg_range(ov, 0x70, 0x79); + /* NOTE: Quantization tables are not readable. You will get the value + * in reg. 0x79 for every table register */ + dump_reg_range(ov, 0x80, 0x9f); + dump_reg_range(ov, 0xa0, 0xbf); + +} + +static void +ov518_dump_regs(struct usb_ov511 *ov) +{ + info("VIDEO MODE REGS"); + dump_reg_range(ov, 0x20, 0x2f); + info("DATA PUMP AND SNAPSHOT REGS"); + dump_reg_range(ov, 0x30, 0x3f); + info("I2C REGS"); + dump_reg_range(ov, 0x40, 0x4f); + info("SYSTEM CONTROL AND VENDOR REGS"); + dump_reg_range(ov, 0x50, 0x5f); + info("60 - 6F"); + dump_reg_range(ov, 0x60, 0x6f); + info("70 - 7F"); + dump_reg_range(ov, 0x70, 0x7f); + info("Y QUANTIZATION TABLE"); + dump_reg_range(ov, 0x80, 0x8f); + info("UV QUANTIZATION TABLE"); + dump_reg_range(ov, 0x90, 0x9f); + info("A0 - BF"); + dump_reg_range(ov, 0xa0, 0xbf); + info("CBR"); + dump_reg_range(ov, 0xc0, 0xcf); +} +#endif + +/*****************************************************************************/ + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline int +ov51x_stop(struct usb_ov511 *ov) +{ + PDEBUG(4, "stopping"); + ov->stopped = 1; + switch (ov->bclass) { + case BCL_OV511: + return (reg_w(ov, R51x_SYS_RESET, 0x3d)); + break; + case BCL_OV518: + return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a)); + break; + case BCL_OV519: + return (reg_w(ov, OV519_SYS_RESET1, 0x0f)); + break; + default: + err("ov51x_stop: invalid bridge type"); + } + return -1; +} + +/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not + * actually stopped (for performance). */ +static inline int +ov51x_restart(struct usb_ov511 *ov) +{ + int rc = 0; + + if (ov->stopped) { + PDEBUG(4, "restarting"); + ov->stopped = 0; + + /* Reinitialize the stream */ + switch (ov->bclass) { + case BCL_OV511: + rc = reg_w(ov, R51x_SYS_RESET, 0x00); + break; + case BCL_OV518: + rc = reg_w(ov, 0x2f, 0x80); + rc = reg_w(ov, R51x_SYS_RESET, 0x00); + break; + case BCL_OV519: + rc = reg_w(ov, OV519_SYS_RESET1, 0x00); + break; + default: + err("ov51x_restart: invalid bridge type"); + rc = -EINVAL; + } + } + + return rc; +} + +/* Sleeps until no frames are active. Returns !0 if got signal */ +static int +ov51x_wait_frames_inactive(struct usb_ov511 *ov) +{ + return wait_event_interruptible(ov->wq, ov->curframe < 0); +} + +/* Resets the hardware snapshot button */ +static void +ov51x_clear_snapshot(struct usb_ov511 *ov) +{ + if (ov->bclass == BCL_OV511) { + reg_w(ov, R51x_SYS_SNAP, 0x00); + reg_w(ov, R51x_SYS_SNAP, 0x02); + reg_w(ov, R51x_SYS_SNAP, 0x00); + } else if (ov->bclass == BCL_OV518) { + warn("snapshot reset not supported yet on OV518(+)"); + } else { + err("clear snap: invalid bridge type"); + } +} + +#if defined(CONFIG_VIDEO_PROC_FS) +/* Checks the status of the snapshot button. Returns 1 if it was pressed since + * it was last cleared, and zero in all other cases (including errors) */ +static int +ov51x_check_snapshot(struct usb_ov511 *ov) +{ + int ret, status = 0; + + if (ov->bclass == BCL_OV511) { + ret = reg_r(ov, R51x_SYS_SNAP); + if (ret < 0) { + err("Error checking snspshot status (%d)", ret); + } else if (ret & 0x08) { + status = 1; + } + } else if (ov->bclass == BCL_OV518) { + warn("snapshot check not supported yet on OV518(+)"); + } else { + err("check snap: invalid bridge type"); + } + + return status; +} +#endif + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 for failure. + */ +static int +init_ov_sensor(struct usb_ov511 *ov) +{ + int i, success; + + /* Reset the sensor */ + if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO; + + /* Wait for it to initialize */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9) + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1 + 150 * HZ / 1000); +#else + msleep(150); +#endif + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && + (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + continue; + } + + /* Reset the sensor */ + if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO; + /* Wait for it to initialize */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9) + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1 + 150 * HZ / 1000); +#else + msleep(150); +#endif + /* Dummy read to sync I2C */ + if (i2c_r(ov, 0x00) < 0) return -EIO; + } + + if (!success) + return -EIO; + + PDEBUG(1, "I2C synced in %d attempt(s)", i); + + return 0; +} + +static int +ov511_set_packet_size(struct usb_ov511 *ov, int size) +{ + int alt, mult; + + if (ov51x_stop(ov) < 0) + return -EIO; + + mult = size >> 5; + + if (ov->bridge == BRG_OV511) { + if (size == 0) alt = OV511_ALT_SIZE_0; + else if (size == 257) alt = OV511_ALT_SIZE_257; + else if (size == 513) alt = OV511_ALT_SIZE_513; + else if (size == 769) alt = OV511_ALT_SIZE_769; + else if (size == 993) alt = OV511_ALT_SIZE_993; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else if (ov->bridge == BRG_OV511PLUS) { + if (size == 0) alt = OV511PLUS_ALT_SIZE_0; + else if (size == 33) alt = OV511PLUS_ALT_SIZE_33; + else if (size == 129) alt = OV511PLUS_ALT_SIZE_129; + else if (size == 257) alt = OV511PLUS_ALT_SIZE_257; + else if (size == 385) alt = OV511PLUS_ALT_SIZE_385; + else if (size == 513) alt = OV511PLUS_ALT_SIZE_513; + else if (size == 769) alt = OV511PLUS_ALT_SIZE_769; + else if (size == 961) alt = OV511PLUS_ALT_SIZE_961; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else { + err("Set packet size: Invalid bridge type"); + return -EINVAL; + } + + PDEBUG(3, "%d, mult=%d, alt=%d", size, mult, alt); + + if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0) + return -EIO; + + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { + err("Set packet size: set interface error"); + return -EBUSY; + } + + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + ov->packet_size = size; + + if (ov51x_restart(ov) < 0) + return -EIO; + + return 0; +} + +/* Note: Unlike the OV511/OV511+, the size argument does NOT include the + * optional packet number byte. The actual size *is* stored in ov->packet_size, + * though. */ +static int +ov518_set_packet_size(struct usb_ov511 *ov, int size) +{ + int alt; + + if (ov51x_stop(ov) < 0) + return -EIO; + + if (ov->bclass == BCL_OV518) { + if (size == 0) alt = OV518_ALT_SIZE_0; + else if (size == 128) alt = OV518_ALT_SIZE_128; + else if (size == 256) alt = OV518_ALT_SIZE_256; + else if (size == 384) alt = OV518_ALT_SIZE_384; + else if (size == 512) alt = OV518_ALT_SIZE_512; + else if (size == 640) alt = OV518_ALT_SIZE_640; + else if (size == 768) alt = OV518_ALT_SIZE_768; + else if (size == 896) alt = OV518_ALT_SIZE_896; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else { + err("Set packet size: Invalid bridge type"); + return -EINVAL; + } + + PDEBUG(3, "%d, alt=%d", size, alt); + + ov->packet_size = size; + if (size > 0) { + /* Program ISO FIFO size reg (packet number isn't included) */ + ov518_reg_w32(ov, 0x30, size, 2); + + if (ov->packet_numbering) + ++ov->packet_size; + } + + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { + err("Set packet size: set interface error"); + return -EBUSY; + } + + /* Initialize the stream */ + if (reg_w(ov, 0x2f, 0x80) < 0) + return -EIO; + + if (ov51x_restart(ov) < 0) + return -EIO; + + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + return 0; +} + +/* Note: Unlike the OV511/OV511+, the size argument does NOT include the + * optional packet number byte. The actual size *is* stored in ov->packet_size, + * though. */ +static int +ov519_set_packet_size(struct usb_ov511 *ov, int size) +{ + int alt; + + if (ov51x_stop(ov) < 0) + return -EIO; + + if (ov->bclass == BCL_OV519) { + if (size == 0) alt = OV519_ALT_SIZE_0; + else if (size == 384) alt = OV519_ALT_SIZE_384; + else if (size == 512) alt = OV519_ALT_SIZE_512; + else if (size == 768) alt = OV519_ALT_SIZE_768; + else if (size == 896) alt = OV519_ALT_SIZE_896; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else { + err("Set packet size: Invalid bridge class"); + return -EINVAL; + } + + PDEBUG(3, "%d, alt=%d", size, alt); + + ov->packet_size = size; + if (size > 0) { + /* Program ISO FIFO size reg (packet number isn't included) */ + //~~~ is this nessecary? ov518_reg_w32(ov, 0x30, size, 2); + + if (ov->packet_numbering) + ++ov->packet_size; + } + + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { + err("Set packet size: set interface error"); + return -EBUSY; + } + + /* Initialize the stream */ + + if (size > 0) { + if (ov51x_restart(ov) < 0) + return -EIO; + } + +// if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) +// return -EIO; + + return 0; +} + +/* Upload compression params and quantization tables. Returns 0 for success. */ +static int +ov511_init_compression(struct usb_ov511 *ov) +{ + int rc = 0; + + if (!ov->compress_inited) { + reg_w(ov, 0x70, phy); + reg_w(ov, 0x71, phuv); + reg_w(ov, 0x72, pvy); + reg_w(ov, 0x73, pvuv); + reg_w(ov, 0x74, qhy); + reg_w(ov, 0x75, qhuv); + reg_w(ov, 0x76, qvy); + reg_w(ov, 0x77, qvuv); + + if (ov511_upload_quan_tables(ov) < 0) { + err("Error uploading quantization tables"); + rc = -EIO; + goto out; + } + } + + ov->compress_inited = 1; +out: + return rc; +} + +/* Upload compression params and quantization tables. Returns 0 for success. */ +static int +ov518_init_compression(struct usb_ov511 *ov) +{ + int rc = 0; + + if (!ov->compress_inited) { + if (ov518_upload_quan_tables(ov) < 0) { + err("Error uploading quantization tables"); + rc = -EIO; + goto out; + } + } + + ov->compress_inited = 1; +out: + return rc; +} + +/* Switch on standard JPEG compression. Returns 0 for success. */ +static int +ov519_init_compression(struct usb_ov511 *ov) +{ + int rc = 0; + + if (!ov->compress_inited) { + if (reg_setbit(ov, OV519_SYS_EN_CLK1, 1, 2 ) < 0) { + err("Error switching to compressed mode"); + rc = -EIO; + goto out; + } + } + + ov->compress_inited = 1; +out: + return rc; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's contrast setting to "val" */ +static int +sensor_set_contrast(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + { + rc = i2c_w(ov, OV7610_REG_CNT, val >> 8); + if (rc < 0) + goto out; + break; + } + case SEN_OV6630: + { + rc = i2c_w_mask(ov, OV7610_REG_CNT, val >> 12, 0x0f); + if (rc < 0) + goto out; + break; + } + case SEN_OV8610: + { + static unsigned char ctab[] = { + 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = i2c_w(ov, 0x64, ctab[val>>13]); + if (rc < 0) + goto out; + break; + } + case SEN_OV7620: + { + static unsigned char ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = i2c_w(ov, 0x64, ctab[val>>12]); + if (rc < 0) + goto out; + break; + } + case SEN_OV7640: + { + /* Use gain control instead. */ + rc = i2c_w(ov, OV7610_REG_GAIN, val>>10); + if (rc < 0) + goto out; + break; + } + case SEN_SAA7111A: + { + rc = i2c_w(ov, 0x0b, val >> 9); + if (rc < 0) + goto out; + break; + } + default: + { + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + } + + rc = 0; /* Success */ + ov->contrast = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's contrast setting */ +static int +sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + rc = i2c_r(ov, OV7610_REG_CNT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_OV6630: + rc = i2c_r(ov, OV7610_REG_CNT); + if (rc < 0) + return rc; + else + *val = rc << 12; + break; + case SEN_OV8610: + { + static unsigned char ctab[] = { + 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f + }; + static int cidx = 0; + + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = i2c_r(ov, 0x64); + if (rc < 0) { + return rc; + } else { + for (cidx = 0; cidx < 8; cidx++) { + if (ctab[cidx] == rc) { + *val = cidx << 13; + break; + } + } + if (cidx == 8) // could not find value in table + return -EINVAL; + } + break; + } + case SEN_OV7620: + /* Use Y gamma reg instead. Bit 0 is the enable bit. */ + rc = i2c_r(ov, 0x64); + if (rc < 0) + return rc; + else + *val = (rc & 0xfe) << 8; + break; + case SEN_OV7640: + /* Use gain control instead. */ + rc = i2c_r(ov, OV7610_REG_GAIN); + if (rc < 0) + return rc; + else + *val = rc << 10; + break; + + case SEN_SAA7111A: + *val = ov->contrast; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->contrast = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's brightness setting to "val" */ +static int +sensor_set_brightness(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(4, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7640: + rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV7620: + /* 7620 doesn't like manual changes when in auto mode */ + if (!ov->auto_brt) { + rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); + if (rc < 0) + goto out; + } + break; + case SEN_SAA7111A: + rc = i2c_w(ov, 0x0a, val >> 8); + if (rc < 0) + goto out; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + + rc = 0; /* Success */ + ov->brightness = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's brightness setting */ +static int +sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV7620: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7640: + rc = i2c_r(ov, OV7610_REG_BRT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_SAA7111A: + *val = ov->brightness; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->brightness = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's saturation (color intensity) setting to "val" */ +static int +sensor_set_saturation(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV7620: +// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e); +// if (rc < 0) +// goto out; + rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV7640: + rc = i2c_w(ov, OV7610_REG_SAT, (val >> 8) & 0xf0); + if (rc < 0) + goto out; + case SEN_SAA7111A: + rc = i2c_w(ov, 0x0c, val >> 9); + if (rc < 0) + goto out; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + + rc = 0; /* Success */ + ov->colour = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's saturation (color intensity) setting */ +static int +sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7640: + rc = i2c_r(ov, OV7610_REG_SAT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_OV7620: +// /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */ +// rc = i2c_r(ov, 0x62); +// if (rc < 0) +// return rc; +// else +// *val = (rc & 0x7e) << 9; + rc = i2c_r(ov, OV7610_REG_SAT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_SAA7111A: + *val = ov->colour; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->colour = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's hue (red/blue balance) setting to "val" */ +static int +sensor_set_hue(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_w(ov, OV7610_REG_RED, 0xFF - (val >> 8)); + if (rc < 0) + goto out; + + rc = i2c_w(ov, OV7610_REG_BLUE, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV8610: + case SEN_OV7640: + rc = i2c_w(ov, OV8610_REG_HUE, (val >> 11) | 0x20); + if (rc < 0) + goto out; + break; + case SEN_OV7620: +// Hue control is causing problems. I will enable it once it's fixed. +#if 0 + rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb); + if (rc < 0) + goto out; + + rc = i2c_w(ov, 0x79, (unsigned char)(val >> 8) + 0xb); + if (rc < 0) + goto out; +#endif + break; + case SEN_SAA7111A: + rc = i2c_w(ov, 0x0d, (val + 32768) >> 8); + if (rc < 0) + goto out; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + + rc = 0; /* Success */ + ov->hue = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's hue (red/blue balance) setting */ +static int +sensor_get_hue(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_r(ov, OV7610_REG_BLUE); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_OV8610: + case SEN_OV7640: + rc = i2c_r(ov, OV8610_REG_HUE); + if (rc < 0) + return rc; + else + *val = (rc & 0x1f) << 11; + break; + case SEN_OV7620: + rc = i2c_r(ov, 0x7a); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_SAA7111A: + *val = ov->hue; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->hue = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int +sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p) +{ + int rc; + + PDEBUG(4, "sensor_set_picture"); + + ov->whiteness = p->whiteness; + + /* Don't return error if a setting is unsupported, or rest of settings + * will not be performed */ + + rc = sensor_set_contrast(ov, p->contrast); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_brightness(ov, p->brightness); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_saturation(ov, p->colour); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_hue(ov, p->hue); + if (FATAL_ERROR(rc)) + return rc; + + return 0; +} + +static int +sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) +{ + int rc; + + PDEBUG(4, "sensor_get_picture"); + + /* Don't return error if a setting is unsupported, or rest of settings + * will not be performed */ + + rc = sensor_get_contrast(ov, &(p->contrast)); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_get_brightness(ov, &(p->brightness)); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_get_saturation(ov, &(p->colour)); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_get_hue(ov, &(p->hue)); + if (FATAL_ERROR(rc)) + return rc; + + p->whiteness = 105 << 8; + + return 0; +} + +#if defined(CONFIG_VIDEO_PROC_FS) +// FIXME: Exposure range is only 0x00-0x7f in interlace mode +/* Sets current exposure for sensor. This only has an effect if auto-exposure + * is off */ +static inline int +sensor_set_exposure(struct usb_ov511 *ov, unsigned char val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7620: + case SEN_OV7640: + case SEN_OV76BE: + case SEN_OV8600: + case SEN_OV8610: + rc = i2c_w(ov, 0x10, val); + if (rc < 0) + goto out; + + break; + case SEN_SAA7111A: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_exposure"); + return -EINVAL; + } + + rc = 0; /* Success */ + ov->exposure = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets current exposure level from sensor, regardless of whether it is under + * manual control. */ +static int +sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7620: + case SEN_OV7640: + case SEN_OV76BE: + case SEN_OV8600: + case SEN_OV8610: + rc = i2c_r(ov, 0x10); + if (rc < 0) + return rc; + else + *val = rc; + break; + case SEN_SAA7111A: + val = 0; + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for get_exposure"); + return -EINVAL; + } + + PDEBUG(3, "%d", *val); + ov->exposure = *val; + + return 0; +} +#endif /* CONFIG_VIDEO_PROC_FS */ + +/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ +static void +ov51x_led_control(struct usb_ov511 *ov, int on) +{ + PDEBUG(4, " (%s)", on ? "turn on" : "turn off"); + + if (ov->bridge == BRG_OV511PLUS) + reg_w(ov, R511_SYS_LED_CTL, on ? 1 : 0); + else if (ov->bridge == BRG_OV519) + reg_w_mask(ov, OV519_GPIO_DATA_OUT0, on ? 0x01 : 0x00, 0x01); + else if (ov->bclass == BCL_OV518) + reg_w_mask(ov, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); + + return; +} + +/* Matches the sensor's internal frame rate to the lighting frequency. + * Valid frequencies are: + * 50 - 50Hz, for European and Asian lighting + * 60 - 60Hz, for American lighting + * + * Tested with: OV7610, OV7620, OV76BE, OV6620 + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_light_freq(struct usb_ov511 *ov, int freq) +{ + int sixty; + + PDEBUG(4, "%d Hz", freq); + + if (freq == 60) + sixty = 1; + else if (freq == 50) + sixty = 0; + else { + err("Invalid light freq (%d Hz)", freq); + return -EINVAL; + } + + switch (ov->sensor) { + case SEN_OV8610: + i2c_w(ov, 0x2b, sixty?0xcc:0xc0); + i2c_w(ov, 0x2a, sixty?0x80:0xa0); + break; + case SEN_OV7610: + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + i2c_w_mask(ov, 0x13, 0x10, 0x10); + i2c_w_mask(ov, 0x13, 0x00, 0x10); + break; + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + i2c_w_mask(ov, 0x76, 0x01, 0x01); + break; + case SEN_OV7640: + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + case SEN_OV6620: + case SEN_OV6630: + i2c_w(ov, 0x2b, sixty?0xa8:0x28); + i2c_w(ov, 0x2a, sixty?0x84:0xa4); + break; + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_light_freq"); + return -EINVAL; + } + + ov->lightfreq = freq; + + return 0; +} + +/* If enable is true, turn on the sensor's banding filter, otherwise turn it + * off. This filter tries to reduce the pattern of horizontal light/dark bands + * caused by some (usually fluorescent) lighting. The light frequency must be + * set either before or after enabling it with ov51x_set_light_freq(). + * + * Tested with: OV7610, OV7620, OV76BE, OV6620. + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_banding_filter(struct usb_ov511 *ov, int enable) +{ + int rc; + + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B + || ov->sensor == SEN_SAA7111A) { + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + } + + rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04); + if (rc < 0) + return rc; + + ov->bandfilt = enable; + + return 0; +} + +/* If enable is true, turn on the sensor's auto brightness control, otherwise + * turn it off. + * + * Unsupported: KS0127, KS0127B, SAA7111A, OV7640 + * Returns: 0 for success + */ +static int +sensor_set_auto_brightness(struct usb_ov511 *ov, int enable) +{ + int rc; + + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B + || ov->sensor == SEN_SAA7111A || ov->sensor == SEN_OV7640) { + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + } + + rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10); + if (rc < 0) + return rc; + + ov->auto_brt = enable; + + return 0; +} + +/* If enable is true, turn on the sensor's auto exposure control, otherwise + * turn it off. + * + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV7610: + i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80); + break; + case SEN_OV6620: + case SEN_OV7620: + case SEN_OV7640: + case SEN_OV76BE: + case SEN_OV8600: + i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); + break; + case SEN_OV6630: + case SEN_OV8610: + i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10); + break; + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_auto_exposure"); + return -EINVAL; + } + + ov->auto_exp = enable; + + return 0; +} + +/* If enable is true, turn on the sensor's auto gain control, otherwise + * turn it off. + * + * Returns: 0 for success + */ +static int +sensor_set_auto_gain(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV7640: + i2c_w_mask(ov, 0x13, enable?0x02:0x00, 0x02); + break; + default: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; +// default: +// err("Sensor not supported for set_auto_gain"); +// return -EINVAL; + } + + ov->auto_gain = enable; + + return 0; +} + +/* Modifies the sensor's exposure algorithm to allow proper exposure of objects + * that are illuminated from behind. + * + * Tested with: OV6620, OV7620 + * Unsupported: OV7610, OV7640, OV76BE, KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_backlight(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV8610: + // change AEC/AGC Reference level + i2c_w_mask(ov, 0x68, enable?0xef:0xcf, 0xff); + // select central 1/4 image to calculate AEC/AGC + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + // increase gain 3dB + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); + break; + case SEN_OV7620: + case SEN_OV8600: + // change AEC/AGC Reference level + i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0); + // select central 1/4 image to calculate AEC/AGC + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + // increase gain 3dB + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); + break; + case SEN_OV6620: + i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80); + break; + case SEN_OV6630: + i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); + break; + case SEN_OV7610: + case SEN_OV7640: + case SEN_OV76BE: + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_backlight"); + return -EINVAL; + } + + ov->backlight = enable; + + return 0; +} + +static int +sensor_set_mirror(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV7640: + case SEN_OV8600: + case SEN_OV8610: + i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40); + break; + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_mirror"); + return -EINVAL; + } + + ov->mirror = enable; + + return 0; +} + +/* Returns number of bits per pixel (regardless of where they are located; + * planar or not), or zero for unsupported format. + */ +static inline int +get_depth(int palette) +{ + switch (palette) { + case VIDEO_PALETTE_GREY: return 8; + case VIDEO_PALETTE_YUV420: return 12; + case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ +#ifdef OV511_ALLOW_CONVERSION + case VIDEO_PALETTE_RGB565: return 16; + case VIDEO_PALETTE_RGB24: return 24; + case VIDEO_PALETTE_YUV422: return 16; + case VIDEO_PALETTE_YUYV: return 16; + case VIDEO_PALETTE_YUV422P: return 16; /* Planar */ +#endif + default: return 0; /* Invalid format */ + } +} + +/* Bytes per frame. Used by read(). Return of 0 indicates error */ +static inline long int +get_frame_length(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + if (!frame) { + return 0; + } else { + if (ov->bclass == BCL_OV519) { + return (frame->bytes_recvd); + } else { + return ((frame->width * frame->height + * get_depth(frame->format)) >> 3); + } + } +} + +static int +mode_init_ov_sensor_regs(struct usb_ov511 *ov, struct ovsensor_window *win) +{ + int qvga = win->quarter; + + /******** Mode (VGA/QVGA) and sensor specific regs ********/ + + switch (ov->sensor) { + case SEN_OV8610: + // For OV8610 qvga means qsvga + i2c_setbit(ov, OV7610_REG_COM_C, qvga?1:0, 5); + // FIXME: Does this improve the image quality or frame rate? +#if 0 + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, 0x10); + i2c_w(ov, 0x25, qvga?0x40:0x8a); + i2c_w(ov, 0x2f, qvga?0x30:0xb0); + i2c_w(ov, 0x35, qvga?0x1c:0x9c); +#endif + break; + case SEN_OV7610: + i2c_w_mask(ov, 0x14, qvga?0x20:0x00, 0x20); +// FIXME: Does this improve the image quality or frame rate? +#if 0 + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, 0x10); + i2c_w(ov, 0x25, qvga?0x40:0x8a); + i2c_w(ov, 0x2f, qvga?0x30:0xb0); + i2c_w(ov, 0x35, qvga?0x1c:0x9c); +#endif + break; + case SEN_OV7620: +// i2c_w(ov, 0x2b, 0x00); + i2c_w_mask(ov, 0x14, qvga?0x20:0x00, 0x20); + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, qvga?0x20:0x3a); + i2c_w(ov, 0x25, qvga?0x30:0x60); + i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); + i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); + i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); + break; + case SEN_OV76BE: +// i2c_w(ov, 0x2b, 0x00); + i2c_w_mask(ov, 0x14, qvga?0x20:0x00, 0x20); +// FIXME: Enable this once 7620AE uses 7620 initial settings +#if 0 + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, qvga?0x20:0x3a); + i2c_w(ov, 0x25, qvga?0x30:0x60); + i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); + i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0); + i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); +#endif + break; + case SEN_OV7640: +// i2c_w(ov, 0x2b, 0x00); + i2c_w_mask(ov, 0x14, qvga?0x20:0x00, 0x20); + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); +// i2c_w(ov, 0x24, qvga?0x20:0x3a); +// i2c_w(ov, 0x25, qvga?0x30:0x60); +// i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); +// i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); +// i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); + break; + case SEN_OV6620: + i2c_w_mask(ov, 0x14, qvga?0x20:0x00, 0x20); + break; + case SEN_OV6630: + i2c_w_mask(ov, 0x14, qvga?0x20:0x00, 0x20); + break; + default: + return -EINVAL; + } + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { + /* these aren't valid on the OV6620/OV7620/6630? */ + i2c_w_mask(ov, 0x0e, 0x40, 0x40); + } + + /* OV6630 default reg 0x13 value is always right */ + /* OV7640 is 8-bit only */ + if (ov->sensor != SEN_OV6630 && ov->sensor != SEN_OV7640) + i2c_w_mask(ov, 0x13, 0x20, 0x20); + else + return -EINVAL; /* No OV6630 greyscale support yet */ + } else { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { + /* not valid on the OV6620/OV7620/6630? */ + i2c_w_mask(ov, 0x0e, 0x00, 0x40); + } + + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + /* OV7640 is 8-bit only */ + + if (ov->sensor != SEN_OV6630 && ov->sensor != SEN_OV7640) + i2c_w_mask(ov, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (ov->sensor == SEN_OV6620) { + /* Clock down */ + + i2c_w(ov, 0x2a, 0x04); + i2c_w(ov, 0x11, win->clockdiv); + i2c_w(ov, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + i2c_w(ov, 0x2d, 0x85); + } else if (win->clockdiv >= 0) { + i2c_w(ov, 0x11, win->clockdiv); + } + + /******** Special Features ********/ + + if (framedrop >= 0 && ov->sensor != SEN_OV7640) + i2c_w(ov, 0x16, framedrop); + + /* Test Pattern */ + if (ov->sensor != SEN_OV7640) + i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02); + + /* Enable auto white balance */ + i2c_w_mask(ov, 0x12, 0x04, 0x04); + + // This will go away as soon as ov51x_mode_init_sensor_regs() + // is fully tested. + /* 7620/6620/6630? don't have register 0x35, so play it safe */ + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { + if (win->width == 640 && win->height == 480) + i2c_w(ov, 0x35, 0x9e); + else + i2c_w(ov, 0x35, 0x1e); + } + + return 0; +} + +static int +set_ov_sensor_window(struct usb_ov511 *ov, struct ovsensor_window *win) +{ + int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale, ret; + + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */ + switch (ov->sensor) { + case SEN_OV8610: + hwsbase = 0x1e; + hwebase = 0x1e; + vwsbase = 0x02; + vwebase = 0x02; + break; + case SEN_OV7610: + case SEN_OV76BE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + break; + case SEN_OV7620: + hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ + hwebase = 0x2f; + vwsbase = vwebase = 0x05; + break; + case SEN_OV7640: + hwsbase = 0x1a; + hwebase = 0x1a; + vwsbase = vwebase = 0x03; + break; + default: + return -EINVAL; + } + + switch (ov->sensor) { + case SEN_OV6620: + case SEN_OV6630: + if (win->quarter) { /* QCIF */ + hwscale = 0; + vwscale = 0; + } else { /* CIF */ + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + break; + case SEN_OV8610: + if (win->quarter) { /* QSVGA */ + hwscale = 1; + vwscale = 1; + } else { /* SVGA */ + hwscale = 2; + vwscale = 2; + } + break; + default: //SEN_OV7xx0 + if (win->quarter) { /* QVGA */ + hwscale = 1; + vwscale = 0; + } else { /* VGA */ + hwscale = 2; + vwscale = 1; + } + } + + ret = mode_init_ov_sensor_regs(ov, win); + if (ret < 0) + return ret; + + if (ov->sensor == SEN_OV8610) { + i2c_w_mask(ov, 0x2d, 0x05, 0x40); /* old 0x95, new 0x05 from windrv 090403 *//* bits 5-7: reserved */ + i2c_w_mask(ov, 0x28, 0x20, 0x20); /* bit 5: progressive mode on */ + } + + i2c_w(ov, 0x17, hwsbase + (win->x >> hwscale)); + i2c_w(ov, 0x18, hwebase + ((win->x + win->width) >> hwscale)); + i2c_w(ov, 0x19, vwsbase + (win->y >> vwscale)); + i2c_w(ov, 0x1a, vwebase + ((win->y + win->height) >> vwscale)); + +#ifdef OV511_DEBUG + if (dump_sensor) + dump_i2c_regs(ov); +#endif + + return 0; +} + +static int +ov_sensor_mode_setup(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + struct ovsensor_window win; + int half_w = ov->maxwidth / 2; + int half_h = ov->maxheight / 2; + + win.format = mode; + + /* Unless subcapture is enabled, center the image window and downsample + * if possible to increase the field of view */ + if (sub_flag) { + win.x = ov->subx; + win.y = ov->suby; + win.width = ov->subw; + win.height = ov->subh; + win.quarter = 0; + } else { + /* NOTE: OV518(+) and OV519 does downsampling on its own */ + if ((width > half_w && height > half_h) + || (ov->bclass == BCL_OV518) + || (ov->bclass == BCL_OV519)) { + win.width = ov->maxwidth; + win.height = ov->maxheight; + win.quarter = 0; + } else if (width > half_w || height > half_h) { + err("Illegal dimensions"); + return -EINVAL; + } else { + win.width = half_w; + win.height = half_h; + win.quarter = 1; + } + + /* Center it */ + win.x = (win.width - width) / 2; + win.y = (win.height - height) / 2; + } + + if (clockdiv >= 0) { + /* Manual override */ + win.clockdiv = clockdiv; + } else if (ov->bridge == BRG_OV518) { + /* OV518 controls the clock externally */ + win.clockdiv = 0; + } else if (ov->bridge == BRG_OV518PLUS) { + /* OV518+ controls the clock externally */ + win.clockdiv = 1; + } else if (ov->bridge == BRG_OV519) { + /* Clock is determined by OV519 frame rate code */ + win.clockdiv = ov->clockdiv; + } else if (ov->compress) { + /* Use the highest possible rate, to maximize FPS */ + switch (ov->sensor) { + case SEN_OV6620: + /* ...except with this sensor, which doesn't like + * higher rates yet */ + win.clockdiv = 3; + break; + case SEN_OV6630: + win.clockdiv = 0; + break; + case SEN_OV76BE: + case SEN_OV7610: + case SEN_OV7620: + win.clockdiv = 1; + break; + case SEN_OV8610: + win.clockdiv = 0; + break; + default: + err("Invalid sensor"); + return -EINVAL; + } + } else { + switch (ov->sensor) { + case SEN_OV6620: + case SEN_OV6630: + win.clockdiv = 3; + break; + case SEN_OV76BE: + case SEN_OV7610: + case SEN_OV7620: + /* Use slowest possible clock without sacrificing + * frame rate */ + win.clockdiv = ((sub_flag ? ov->subw * ov->subh + : width * height) + * (win.format == VIDEO_PALETTE_GREY ? 2 : 3) / 2) + / 66000; + break; + default: + err("Invalid sensor"); + return -EINVAL; + } + } + + PDEBUG(4, "Setting clock divider to %d", win.clockdiv); + + return set_ov_sensor_window(ov, &win); +} + +/* Set up the OV511/OV511+ with the given image parameters. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int +ov511_mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + int hsegs, vsegs; + + if (sub_flag) { + width = ov->subw; + height = ov->subh; + } + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + // FIXME: This should be moved to a 7111a-specific function once + // subcapture is dealt with properly + if (ov->sensor == SEN_SAA7111A) { + if (width == 320 && height == 240) { + /* No need to do anything special */ + } else if (width == 640 && height == 480) { + /* Set the OV511 up as 320x480, but keep the + * V4L resolution as 640x480 */ + width = 320; + } else { + err("SAA7111A only allows 320x240 or 640x480"); + return -EINVAL; + } + } + + /* Make sure width and height are a multiple of 8 */ + if (width % 8 || height % 8) { + err("Invalid size (%d, %d) (mode = %d)", width, height, mode); + return -EINVAL; + } + + if (width < ov->minwidth || height < ov->minheight) { + err("Requested dimensions are too small"); + return -EINVAL; + } + + if (ov51x_stop(ov) < 0) + return -EIO; + + if (mode == VIDEO_PALETTE_GREY) { + reg_w(ov, R511_CAM_UV_EN, 0x00); + reg_w(ov, R511_SNAP_UV_EN, 0x00); + reg_w(ov, R511_SNAP_OPTS, 0x01); + } else { + reg_w(ov, R511_CAM_UV_EN, 0x01); + reg_w(ov, R511_SNAP_UV_EN, 0x01); + reg_w(ov, R511_SNAP_OPTS, 0x03); + } + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + hsegs = (width >> 3) - 1; + vsegs = (height >> 3) - 1; + + reg_w(ov, R511_CAM_PXCNT, hsegs); + reg_w(ov, R511_CAM_LNCNT, vsegs); + reg_w(ov, R511_CAM_PXDIV, 0x00); + reg_w(ov, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filter on */ + reg_w(ov, R511_CAM_OPTS, 0x03); + + /* Snapshot additions */ + reg_w(ov, R511_SNAP_PXCNT, hsegs); + reg_w(ov, R511_SNAP_LNCNT, vsegs); + reg_w(ov, R511_SNAP_PXDIV, 0x00); + reg_w(ov, R511_SNAP_LNDIV, 0x00); + + if (ov->compress) { + /* Enable Y and UV quantization and compression */ + reg_w(ov, R511_COMP_EN, 0x07); + reg_w(ov, R511_COMP_LUT_EN, 0x03); + ov51x_reset(ov, OV511_RESET_OMNICE); + } + + if (ov51x_restart(ov) < 0) + return -EIO; + + return 0; +} + +/* Sets up the OV518/OV518+ with the given image parameters + * + * OV518 needs a completely different approach, until we can figure out what + * the individual registers do. Also, only 15 FPS is supported now. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int +ov518_mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + int hsegs, vsegs, hi_res; + + if (sub_flag) { + width = ov->subw; + height = ov->subh; + } + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + if (width % 16 || height % 8) { + err("Invalid size (%d, %d)", width, height); + return -EINVAL; + } + + if (width < ov->minwidth || height < ov->minheight) { + err("Requested dimensions are too small"); + return -EINVAL; + } + + if (width >= 320 && height >= 240) { + hi_res = 1; + } else if (width >= 320 || height >= 240) { + err("Invalid width/height combination (%d, %d)", width, height); + return -EINVAL; + } else { + hi_res = 0; + } + + if (ov51x_stop(ov) < 0) + return -EIO; + + /******** Set the mode ********/ + + reg_w(ov, 0x2b, 0); + reg_w(ov, 0x2c, 0); + reg_w(ov, 0x2d, 0); + reg_w(ov, 0x2e, 0); + reg_w(ov, 0x3b, 0); + reg_w(ov, 0x3c, 0); + reg_w(ov, 0x3d, 0); + reg_w(ov, 0x3e, 0); + + if (ov->bridge == BRG_OV518 && ov518_color) { + if (mode == VIDEO_PALETTE_GREY) { + /* Set 16-bit input format (UV data are ignored) */ + reg_w_mask(ov, 0x20, 0x00, 0x08); + + /* Set 8-bit (4:0:0) output format */ + reg_w_mask(ov, 0x28, 0x00, 0xf0); + reg_w_mask(ov, 0x38, 0x00, 0xf0); + } else { + /* Set 8-bit (YVYU) input format */ + reg_w_mask(ov, 0x20, 0x08, 0x08); + + /* Set 12-bit (4:2:0) output format */ + reg_w_mask(ov, 0x28, 0x80, 0xf0); + reg_w_mask(ov, 0x38, 0x80, 0xf0); + } + } else { + reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); + reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); + } + + hsegs = width / 16; + vsegs = height / 4; + + reg_w(ov, 0x29, hsegs); + reg_w(ov, 0x2a, vsegs); + + reg_w(ov, 0x39, hsegs); + reg_w(ov, 0x3a, vsegs); + + /* Windows driver does this here; who knows why */ + reg_w(ov, 0x2f, 0x80); + + /******** Set the framerate (to 30 FPS) ********/ + + /* Mode independent, but framerate dependent, regs */ + reg_w(ov, 0x51, 0x04); /* Clock divider; lower==faster */ + reg_w(ov, 0x22, 0x18); + reg_w(ov, 0x23, 0xff); + + if (ov->bridge == BRG_OV518PLUS) + reg_w(ov, 0x21, 0x19); + else + reg_w(ov, 0x71, 0x17); /* Compression-related? */ + + // FIXME: Sensor-specific + /* Bit 5 is what matters here. Of course, it is "reserved" */ + i2c_w(ov, 0x54, 0x23); + + reg_w(ov, 0x2f, 0x80); + + if (ov->bridge == BRG_OV518PLUS) { + reg_w(ov, 0x24, 0x94); + reg_w(ov, 0x25, 0x90); + ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */ + ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */ + ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */ + ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ + ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ + ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */ + ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ + ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ + } else { + reg_w(ov, 0x24, 0x9f); + reg_w(ov, 0x25, 0x90); + ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(ov, 0xc6, 381, 2); /* 17dh */ + ov518_reg_w32(ov, 0xc7, 381, 2); /* 17dh */ + ov518_reg_w32(ov, 0xc8, 128, 2); /* 80h */ + ov518_reg_w32(ov, 0xca, 183331, 3); /* 2cc23h */ + ov518_reg_w32(ov, 0xcb, 746, 2); /* 2eah */ + ov518_reg_w32(ov, 0xcc, 1750, 2); /* 6d6h */ + ov518_reg_w32(ov, 0xcd, 45, 2); /* 2dh */ + ov518_reg_w32(ov, 0xce, 851, 2); /* 353h */ + } + + reg_w(ov, 0x2f, 0x80); + + if (ov51x_restart(ov) < 0) + return -EIO; + + /* Reset it just for good measure */ + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + return 0; +} + +/* Sets up the OV519 with the given image parameters + * + * OV519 needs a completely different approach, until we can figure out what + * the individual registers do. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int +ov519_mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + static struct ov511_regvals regvals_mode_init_519[] = { + { OV511_REG_BUS, 0x5d, 0x03 }, /* Turn off suspend mode */ + { OV511_REG_BUS, 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { OV511_REG_BUS, 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { OV511_REG_BUS, 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { OV511_REG_BUS, 0xa3, 0x18 }, + { OV511_REG_BUS, 0xa4, 0x04 }, + { OV511_REG_BUS, 0xa5, 0x28 }, + { OV511_REG_BUS, 0x37, 0x00 }, /* SetUsbInit */ + { OV511_REG_BUS, 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { OV511_REG_BUS, 0x22, 0x1d }, + { OV511_REG_BUS, 0x17, 0x50 }, /* undocumented */ + { OV511_REG_BUS, 0x37, 0x00 }, /* undocumented */ + { OV511_REG_BUS, 0x40, 0xff }, /* I2C timeout counter */ + { OV511_REG_BUS, 0x46, 0x00 }, /* I2C clock prescaler */ + { OV511_REG_BUS, 0x59, 0x04 }, /* new from windrv 090403 */ + { OV511_REG_BUS, 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + { OV511_DONE_BUS, 0x0, 0x00}, + }; + +// int hi_res; + + if (sub_flag) { + width = ov->subw; + height = ov->subh; + } + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + if ((width % 16) || (height % 8)) { + err("Invalid size (%d, %d)", width, height); + return -EINVAL; + } + + if (width < ov->minwidth || height < ov->minheight) { + err("Requested dimensions are too small %dx%d", width, height); + return -EINVAL; + } + +// if (width >= 800 && height >= 600) { +// hi_res = 1; +// } else + if (width > ov->maxwidth || height > ov->maxheight) { + err("Requested dimensions are too big %dx%d", width, height); + return -EINVAL; + } +// else { +// hi_res = 0; +// } + + if (ov51x_stop(ov) < 0) + return -EIO; + + /******** Set the mode ********/ + + if (write_regvals(ov, regvals_mode_init_519)) + return -EIO; + + if (ov->sensor == SEN_OV7640) { + /* Select 8-bit input mode */ + reg_w_mask(ov, OV519_CAM_DFR, 0x10, 0x10); + } + + reg_w(ov, OV519_CAM_H_SIZE, width>>4); + reg_w(ov, OV519_CAM_V_SIZE, height>>3); + reg_w(ov, OV519_CAM_X_OFFSETL, 0x00); + reg_w(ov, OV519_CAM_X_OFFSETH, 0x00); + reg_w(ov, OV519_CAM_Y_OFFSETL, 0x00); + reg_w(ov, OV519_CAM_Y_OFFSETH, 0x00); + reg_w(ov, OV519_CAM_DIVIDER, 0x00); + reg_w(ov, OV519_CAM_FORMAT, 0x03); /* YUV422 */ + reg_w(ov, 0x26, 0x00); /* Undocumented */ + + + /******** Set the framerate ********/ + if (framerate > 0) { + ov->framerate = framerate; + } + +// FIXME: These are only valid at the max resolution. + if (ov->sensor == SEN_OV7640) { + switch (ov->framerate) { + case 30: + reg_w(ov, 0xa4, 0x0c); + reg_w(ov, 0x23, 0xff); + ov->clockdiv = 0; + break; + case 25: + reg_w(ov, 0xa4, 0x0c); + reg_w(ov, 0x23, 0x1f); + ov->clockdiv = 0; + break; + case 20: + reg_w(ov, 0xa4, 0x0c); + reg_w(ov, 0x23, 0x1b); + ov->clockdiv = 0; + break; + case 15: + reg_w(ov, 0xa4, 0x04); + reg_w(ov, 0x23, 0xff); + ov->clockdiv = 1; + break; + case 10: + reg_w(ov, 0xa4, 0x04); + reg_w(ov, 0x23, 0x1f); + ov->clockdiv = 1; + break; + case 5: + reg_w(ov, 0xa4, 0x04); + reg_w(ov, 0x23, 0x1b); + ov->clockdiv = 1; + break; + default: // 30 fps + reg_w(ov, 0xa4, 0x0c); + reg_w(ov, 0x23, 0xff); + ov->clockdiv = 0; + } + } else if (ov->sensor == SEN_OV8610) { + switch (ov->framerate) { + case 15: + reg_w(ov, 0xa4, 0x06); + reg_w(ov, 0x23, 0xff); + break; + case 10: + reg_w(ov, 0xa4, 0x06); + reg_w(ov, 0x23, 0x1f); + break; + case 5: + reg_w(ov, 0xa4, 0x06); + reg_w(ov, 0x23, 0x1b); + break; + default: // 15 fps + reg_w(ov, 0xa4, 0x06); + reg_w(ov, 0x23, 0xff); + } + + ov->clockdiv = 0; + } else { + err("Sensor not supported for OV519!"); + return -EINVAL; + } + + if (ov51x_restart(ov) < 0) + return -EIO; + + /* Reset it just for good measure */ + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + return 0; +} + +/* This is a wrapper around the OV511, OV518, and sensor specific functions */ +static int +mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + int rc = 0; + + if (!ov || !ov->dev) + return -EFAULT; + + switch (ov->bclass) { + case BCL_OV511: + rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag); + break; + case BCL_OV518: + rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag); + break; + case BCL_OV519: + rc = ov519_mode_init_regs(ov, width, height, mode, sub_flag); + break; + default: + err("mode_init_regs: Invalid bridge class"); + rc = -EINVAL; + } + + if (FATAL_ERROR(rc)) + return rc; + + switch (ov->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV7640: + case SEN_OV8600: + case SEN_OV6620: + case SEN_OV6630: + rc = ov_sensor_mode_setup(ov, width, height, mode, sub_flag); + break; + + case SEN_SAA7111A: +// rc = mode_init_saa_sensor_regs(ov, width, height, mode, +// sub_flag); + + PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f)); + break; + default: + rc = -EINVAL; + } + + if (FATAL_ERROR(rc)) + return rc; + + /* Sensor-independent settings */ + rc = sensor_set_auto_brightness(ov, ov->auto_brt); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_auto_exposure(ov, ov->auto_exp); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_auto_gain(ov, ov->auto_gain); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_banding_filter(ov, bandingfilter); + if (FATAL_ERROR(rc)) + return rc; + + if (ov->lightfreq) { + rc = sensor_set_light_freq(ov, lightfreq); + if (FATAL_ERROR(rc)) + return rc; + } + + rc = sensor_set_backlight(ov, ov->backlight); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_mirror(ov, ov->mirror); + if (FATAL_ERROR(rc)) + return rc; + + return 0; +} + +/* This sets the default image parameters. This is useful for apps that use + * read() and do not set these. + */ +static int +ov51x_set_default_params(struct usb_ov511 *ov) +{ + int i; + + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used + * (using read() instead). */ + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].width = ov->maxwidth; + ov->frame[i].height = ov->maxheight; + ov->frame[i].bytes_read = 0; + if (force_palette) + ov->frame[i].format = force_palette; + else +#ifdef OV511_ALLOW_CONVERSION + ov->frame[i].format = VIDEO_PALETTE_RGB24; +#else + ov->frame[i].format = VIDEO_PALETTE_YUV420; +#endif + ov->frame[i].depth = get_depth(ov->frame[i].format); + } + + PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight, + symbolic(v4l1_plist, ov->frame[0].format)); + + /* Initialize to max width/height, YUV420 or RGB24 (if supported) */ + if (mode_init_regs(ov, ov->maxwidth, ov->maxheight, + ov->frame[0].format, 0) < 0) + return -EINVAL; + + return 0; +} + +/********************************************************************** + * + * Video decoder stuff + * + **********************************************************************/ + +/* Set analog input port of decoder */ +static int +decoder_set_input(struct usb_ov511 *ov, int input) +{ + PDEBUG(4, "port %d", input); + + switch (ov->sensor) { + case SEN_SAA7111A: + { + /* Select mode */ + i2c_w_mask(ov, 0x02, input, 0x07); + /* Bypass chrominance trap for modes 4..7 */ + i2c_w_mask(ov, 0x09, (input > 3) ? 0x80:0x00, 0x80); + break; + } + default: + return -EINVAL; + } + + return 0; +} + +/* Get ASCII name of video input */ +static int +decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) +{ + switch (ov->sensor) { + case SEN_SAA7111A: + { + if (input < 0 || input > 7) + return -EINVAL; + else if (input < 4) + sprintf(name, "CVBS-%d", input); + else // if (input < 8) + sprintf(name, "S-Video-%d", input - 4); + break; + } + default: + sprintf(name, "%s", "Camera"); + } + + return 0; +} + +/* Set norm (NTSC, PAL, SECAM, AUTO) */ +static int +decoder_set_norm(struct usb_ov511 *ov, int norm) +{ + PDEBUG(4, "%d", norm); + + switch (ov->sensor) { + case SEN_SAA7111A: + { + int reg_8, reg_e; + + if (norm == VIDEO_MODE_NTSC) { + reg_8 = 0x40; /* 60 Hz */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ + } else if (norm == VIDEO_MODE_PAL) { + reg_8 = 0x00; /* 50 Hz */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ + } else if (norm == VIDEO_MODE_AUTO) { + reg_8 = 0x80; /* Auto field detect */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ + } else if (norm == VIDEO_MODE_SECAM) { + reg_8 = 0x00; /* 50 Hz */ + reg_e = 0x50; /* SECAM / PAL 4.43 */ + } else { + return -EINVAL; + } + + i2c_w_mask(ov, 0x08, reg_8, 0xc0); + i2c_w_mask(ov, 0x0e, reg_e, 0x70); + break; + } + default: + return -EINVAL; + } + + return 0; +} + +#ifdef OV511_ALLOW_CONVERSION +/********************************************************************** + * + * Color correction functions + * + **********************************************************************/ + +/* + * Turn a YUV4:2:0 block into an RGB block + * + * Video4Linux seems to use the blue, green, red channel + * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red. + * + * Color space conversion coefficients taken from the excellent + * http://www.inforamp.net/~poynton/ColorFAQ.html + * In his terminology, this is a CCIR 601.1 YCbCr -> RGB. + * Y values are given for all 4 pixels, but the U (Pb) + * and V (Pr) are assumed constant over the 2x2 block. + * + * To avoid floating point arithmetic, the color conversion + * coefficients are scaled into 16.16 fixed-point integers. + * They were determined as follows: + * + * double brightness = 1.0; (0->black; 1->full scale) + * double saturation = 1.0; (0->greyscale; 1->full color) + * double fixScale = brightness * 256 * 256; + * int rvScale = (int)(1.402 * saturation * fixScale); + * int guScale = (int)(-0.344136 * saturation * fixScale); + * int gvScale = (int)(-0.714136 * saturation * fixScale); + * int buScale = (int)(1.772 * saturation * fixScale); + * int yScale = (int)(fixScale); + */ + +/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */ +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) + +static inline void +move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, + int rowPixels, unsigned char * rgb, int bits) +{ + const int rvScale = 91881; + const int guScale = -22553; + const int gvScale = -46801; + const int buScale = 116129; + const int yScale = 65536; + int r, g, b; + + g = guScale * u + gvScale * v; + if (force_rgb) { + r = buScale * u; + b = rvScale * v; + } else { + r = rvScale * v; + b = buScale * u; + } + + yTL *= yScale; yTR *= yScale; + yBL *= yScale; yBR *= yScale; + + if (bits == 24) { + /* Write out top two pixels */ + rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); + rgb[2] = LIMIT(r+yTL); + + rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); + rgb[5] = LIMIT(r+yTR); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 3 * rowPixels; + rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); + rgb[2] = LIMIT(r+yBL); + + rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); + rgb[5] = LIMIT(r+yBR); + } else if (bits == 16) { + /* Write out top two pixels */ + rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) + | ((LIMIT(g+yTL) << 3) & 0xE0); + rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) + | (LIMIT(r+yTL) & 0xF8); + + rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) + | ((LIMIT(g+yTR) << 3) & 0xE0); + rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) + | (LIMIT(r+yTR) & 0xF8); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 2 * rowPixels; + + rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) + | ((LIMIT(g+yBL) << 3) & 0xE0); + rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) + | (LIMIT(r+yBL) & 0xF8); + + rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) + | ((LIMIT(g+yBR) << 3) & 0xE0); + rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) + | (LIMIT(r+yBR) & 0xF8); + } +} + +#endif /* OV511_ALLOW_CONVERSION */ + +/********************************************************************** + * + * Raw data parsing + * + **********************************************************************/ + +/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the + * image at pOut is specified by w. + */ +static inline void +make_8x8(unsigned char *pIn, unsigned char *pOut, int w) +{ + unsigned char *pOut1 = pOut; + int x, y; + + for (y = 0; y < 8; y++) { + pOut1 = pOut; + for (x = 0; x < 8; x++) { + *pOut1++ = *pIn++; + } + pOut += w; + } +} + +/* + * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ +static void +yuv400raw_to_yuv400p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int x, y; + unsigned char *pIn, *pOut, *pOutLine; + + /* Copy Y */ + pIn = pIn0; + pOutLine = pOut0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + } + pOutLine += 8 * frame->rawwidth; + } +} + +/* + * For YUV 4:2:0 images, the data show up in 384 byte segments. + * The first 64 bytes of each segment are U, the next 64 are V. The U and + * V are arranged as follows: + * + * 0 1 ... 7 + * 8 9 ... 15 + * ... + * 56 57 ... 63 + * + * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + * + * The next 256 bytes are full resolution Y data and represent 4 squares + * of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 + * + * Note that the U and V data in one segment represent a 16 x 16 pixel + * area, but the Y data represent a 32 x 8 pixel area. If the width is not an + * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the + * next horizontal stripe. + * + * If dumppix module param is set, _parse_data just dumps the incoming segments, + * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 + * this puts the data on the standard output and can be analyzed with the + * parseppm.c utility I wrote. That's a much faster way for figuring out how + * these data are scrambled. + */ + +/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0. + * + * FIXME: Currently only handles width and height that are multiples of 16 + */ +static void +yuv420raw_to_yuv420p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int k, x, y; + unsigned char *pIn, *pOut, *pOutLine; + const unsigned int a = frame->rawwidth * frame->rawheight; + const unsigned int w = frame->rawwidth / 2; + + /* Copy U and V */ + pIn = pIn0; + pOutLine = pOut0 + a; + for (y = 0; y < frame->rawheight - 1; y += 16) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 16) { + make_8x8(pIn, pOut, w); + make_8x8(pIn + 64, pOut + a/4, w); + pIn += 384; + pOut += 8; + } + pOutLine += 8 * w; + } + + /* Copy Y */ + pIn = pIn0 + 128; + pOutLine = pOut0; + k = 0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + if ((++k) > 3) { + k = 0; + pIn += 128; + } + } + pOutLine += 8 * frame->rawwidth; + } +} + +#ifdef OV511_ALLOW_CONVERSION +/* + * fixFrameRGBoffset-- + * My camera seems to return the red channel about 1 pixel + * low, and the blue channel about 1 pixel high. After YUV->RGB + * conversion, we can correct this easily. OSL 2/24/2000. + */ +static void +fixFrameRGBoffset(struct ov511_frame *frame) +{ + int x, y; + int rowBytes = frame->width*3, w = frame->width; + unsigned char *rgb = frame->data; + const int shift = 1; /* Distance to shift pixels by, vertically */ + + /* Don't bother with little images */ + if (frame->width < 400) + return; + + /* This only works with RGB24 */ + if (frame->format != VIDEO_PALETTE_RGB24) + return; + + /* Shift red channel up */ + for (y = shift; y < frame->height; y++) { + int lp = (y-shift)*rowBytes; /* Previous line offset */ + int lc = y*rowBytes; /* Current line offset */ + for (x = 0; x < w; x++) + rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */ + } + + /* Shift blue channel down */ + for (y = frame->height-shift-1; y >= 0; y--) { + int ln = (y + shift) * rowBytes; /* Next line offset */ + int lc = y * rowBytes; /* Current line offset */ + for (x = 0; x < w; x++) + rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */ + } +} +#endif + +/********************************************************************** + * + * Decompression + * + **********************************************************************/ + +/* Chooses a decompression module, locks it, and sets ov->decomp_ops + * accordingly. Returns -ENXIO if decompressor is not available, otherwise + * returns 0 if no other error. + */ +static int +request_decompressor(struct usb_ov511 *ov) +{ + if (!ov) + return -ENODEV; + + if (ov->decomp_ops) { + err("ERROR: Decompressor already requested!"); + return -EINVAL; + } + + lock_kernel(); + + /* Try to get MMX, and fall back on no-MMX if necessary */ + if (ov->bclass == BCL_OV511) { + if (ov511_mmx_decomp_ops) { + PDEBUG(3, "Using OV511 MMX decompressor"); + ov->decomp_ops = ov511_mmx_decomp_ops; + } else if (ov511_decomp_ops) { + PDEBUG(3, "Using OV511 decompressor"); + ov->decomp_ops = ov511_decomp_ops; + } else { + err("No decompressor available"); + } + } else if (ov->bclass == BCL_OV518) { + if (ov518_mmx_decomp_ops) { + PDEBUG(3, "Using OV518 MMX decompressor"); + ov->decomp_ops = ov518_mmx_decomp_ops; + } else if (ov518_decomp_ops) { + PDEBUG(3, "Using OV518 decompressor"); + ov->decomp_ops = ov518_decomp_ops; + } else { + err("No decompressor available"); + } + } else if (ov->bclass == BCL_OV519) { + info("OV519 doesn't need proprietary decompressor. It uses standard JPEG"); + } else { + err("Decompressor: Unknown bridge"); + } + + if (ov->decomp_ops) { + if (!ov->decomp_ops->owner) { + ov->decomp_ops = NULL; + unlock_kernel(); + return -ENOSYS; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + if (! try_module_get (ov->decomp_ops->owner)) { + ov->decomp_ops = NULL; + unlock_kernel(); + return -ENOSYS; + } +#else + __MOD_INC_USE_COUNT(ov->decomp_ops->owner); +#endif + unlock_kernel(); + return 0; + } else { + unlock_kernel(); + if (ov->bclass == BCL_OV519) + return 0; + else + return -ENOSYS; + } +} + +/* Unlocks decompression module and nulls ov->decomp_ops. Safe to call even + * if ov->decomp_ops is NULL. + */ +static void +release_decompressor(struct usb_ov511 *ov) +{ + int released = 0; /* Did we actually do anything? */ + + if (!ov) + return; + + if (ov->bclass == BCL_OV519) { + ov->compress_inited = 0; + return; + } + + lock_kernel(); + + if (ov->decomp_ops && ov->decomp_ops->owner) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + module_put(ov->decomp_ops->owner); +#else + __MOD_DEC_USE_COUNT(ov->decomp_ops->owner); +#endif + released = 1; + } + + ov->decomp_ops = NULL; + + unlock_kernel(); + + if (released) + PDEBUG(3, "Decompressor released"); +} + +static void +decompress(struct usb_ov511 *ov, struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + if (!ov->decomp_ops) + if (request_decompressor(ov)) + return; + + PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd); + + if (frame->format == VIDEO_PALETTE_GREY + && ov->decomp_ops->decomp_400) { + int ret = ov->decomp_ops->decomp_400( + pIn0, + pOut0, + frame->compbuf, + frame->rawwidth, + frame->rawheight, + frame->bytes_recvd); + PDEBUG(4, "DEBUG: decomp_400 returned %d", ret); + } else if (frame->format != VIDEO_PALETTE_GREY + && ov->decomp_ops->decomp_420) { + int ret = ov->decomp_ops->decomp_420( + pIn0, + pOut0, + frame->compbuf, + frame->rawwidth, + frame->rawheight, + frame->bytes_recvd); + PDEBUG(4, "DEBUG: decomp_420 returned %d", ret); + } else { + err("Decompressor does not support this format"); + } +} + +/********************************************************************** + * + * Format conversion + * + **********************************************************************/ + +#ifdef OV511_ALLOW_CONVERSION + +/* Converts from planar YUV420 to RGB24. */ +static void +yuv420p_to_rgb(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0, int bits) +{ + const int numpix = frame->width * frame->height; + const int bytes = bits >> 3; + int i, j, y00, y01, y10, y11, u, v; + unsigned char *pY = pIn0; + unsigned char *pU = pY + numpix; + unsigned char *pV = pU + numpix / 4; + unsigned char *pOut = pOut0; + + for (j = 0; j <= frame->height - 2; j += 2) { + for (i = 0; i <= frame->width - 2; i += 2) { + y00 = *pY; + y01 = *(pY + 1); + y10 = *(pY + frame->width); + y11 = *(pY + frame->width + 1); + u = (*pU++) - 128; + v = (*pV++) - 128; + + move_420_block(y00, y01, y10, y11, u, v, + frame->width, pOut, bits); + + pY += 2; + pOut += 2 * bytes; + } + pY += frame->width; + pOut += frame->width * bytes; + } +} + +/* Converts from planar YUV420 to YUV422 (YUYV). */ +static void +yuv420p_to_yuv422(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + const int numpix = frame->width * frame->height; + int i, j; + unsigned char *pY = pIn0; + unsigned char *pU = pY + numpix; + unsigned char *pV = pU + numpix / 4; + unsigned char *pOut = pOut0; + + for (i = 0; i < numpix; i++) { + *pOut = *(pY + i); + pOut += 2; + } + + pOut = pOut0 + 1; + for (j = 0; j <= frame->height - 2 ; j += 2) { + for (i = 0; i <= frame->width - 2; i += 2) { + int u = *pU++; + int v = *pV++; + + *pOut = u; + *(pOut+2) = v; + *(pOut+frame->width*2) = u; + *(pOut+frame->width*2+2) = v; + pOut += 4; + } + pOut += (frame->width * 2); + } +} + +/* Converts pData from planar YUV420 to planar YUV422 **in place**. */ +static void +yuv420p_to_yuv422p(struct ov511_frame *frame, unsigned char *pData) +{ + const int numpix = frame->width * frame->height; + const int w = frame->width; + int j; + unsigned char *pIn, *pOut; + + /* Clear U and V */ + memset(pData + numpix + numpix / 2, 127, numpix / 2); + + /* Convert V starting from beginning and working forward */ + pIn = pData + numpix + numpix / 4; + pOut = pData + numpix +numpix / 2; + for (j = 0; j <= frame->height - 2; j += 2) { + memmove(pOut, pIn, w/2); + memmove(pOut + w/2, pIn, w/2); + pIn += w/2; + pOut += w; + } + + /* Convert U, starting from end and working backward */ + pIn = pData + numpix + numpix / 4; + pOut = pData + numpix + numpix / 2; + for (j = 0; j <= frame->height - 2; j += 2) { + pIn -= w/2; + pOut -= w; + memmove(pOut, pIn, w/2); + memmove(pOut + w/2, pIn, w/2); + } +} + +#endif /* OV511_ALLOW_CONVERSION */ + +/* Fuses even and odd fields together, and doubles width. + * INPUT: an odd field followed by an even field at pIn0, in YUV planar format + * OUTPUT: a normal YUV planar image, with correct aspect ratio + */ +static void +deinterlace(struct ov511_frame *frame, int rawformat, + unsigned char *pIn0, unsigned char *pOut0) +{ + const int fieldheight = frame->rawheight / 2; + const int fieldpix = fieldheight * frame->rawwidth; + const int w = frame->width; + int x, y; + unsigned char *pInEven, *pInOdd, *pOut; + + PDEBUG(5, "fieldheight=%d", fieldheight); + + if (frame->rawheight != frame->height) { + err("invalid height"); + return; + } + + if ((frame->rawwidth * 2) != frame->width) { + err("invalid width"); + return; + } + + /* Y */ + pInOdd = pIn0; + pInEven = pInOdd + fieldpix; + pOut = pOut0; + for (y = 0; y < fieldheight; y++) { + for (x = 0; x < frame->rawwidth; x++) { + *pOut = *pInEven; + *(pOut+1) = *pInEven++; + *(pOut+w) = *pInOdd; + *(pOut+w+1) = *pInOdd++; + pOut += 2; + } + pOut += w; + } + + if (rawformat == RAWFMT_YUV420) { + /* U */ + pInOdd = pIn0 + fieldpix * 2; + pInEven = pInOdd + fieldpix / 4; + for (y = 0; y < fieldheight / 2; y++) { + for (x = 0; x < frame->rawwidth / 2; x++) { + *pOut = *pInEven; + *(pOut+1) = *pInEven++; + *(pOut+w/2) = *pInOdd; + *(pOut+w/2+1) = *pInOdd++; + pOut += 2; + } + pOut += w/2; + } + /* V */ + pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2; + pInEven = pInOdd + fieldpix / 4; + for (y = 0; y < fieldheight / 2; y++) { + for (x = 0; x < frame->rawwidth / 2; x++) { + *pOut = *pInEven; + *(pOut+1) = *pInEven++; + *(pOut+w/2) = *pInOdd; + *(pOut+w/2+1) = *pInOdd++; + pOut += 2; + } + pOut += w/2; + } + } +} + +static void +ov51x_postprocess_grey(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + /* Deinterlace frame, if necessary */ + if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, + frame->tempdata); + else + yuv400raw_to_yuv400p(frame, frame->rawdata, + frame->tempdata); + + deinterlace(frame, RAWFMT_YUV400, frame->tempdata, + frame->data); + } else { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, + frame->data); + else + yuv400raw_to_yuv400p(frame, frame->rawdata, + frame->data); + } +} + +#ifdef OV511_ALLOW_CONVERSION +/* Process raw YUV420 data into the format requested by the app. Conversion + * between V4L formats is allowed. + */ +static void +ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + /* Process frame->rawdata to frame->tempdata */ + if (frame->compressed) + decompress(ov, frame, frame->rawdata, frame->tempdata); + else + yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata); + + /* Deinterlace frame, if necessary */ + if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { + memcpy(frame->rawdata, frame->tempdata, + MAX_RAW_DATA_SIZE(frame->width, frame->height)); + deinterlace(frame, RAWFMT_YUV420, frame->rawdata, + frame->tempdata); + } + + /* Frame should be (width x height) and not (rawwidth x rawheight) at + * this point. */ + + /* Process frame->tempdata to frame->data */ + switch (frame->format) { + case VIDEO_PALETTE_RGB565: + yuv420p_to_rgb(frame, frame->tempdata, frame->data, 16); + break; + case VIDEO_PALETTE_RGB24: + yuv420p_to_rgb(frame, frame->tempdata, frame->data, 24); + break; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + yuv420p_to_yuv422(frame, frame->tempdata, frame->data); + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + memcpy(frame->data, frame->tempdata, + MAX_RAW_DATA_SIZE(frame->width, frame->height)); + break; + case VIDEO_PALETTE_YUV422P: + /* Data is converted in place, so copy it in advance */ + memcpy(frame->data, frame->tempdata, + MAX_RAW_DATA_SIZE(frame->width, frame->height)); + + yuv420p_to_yuv422p(frame, frame->data); + break; + default: + err("Cannot convert YUV420 to %s", + symbolic(v4l1_plist, frame->format)); + } + + if (fix_rgb_offset) + fixFrameRGBoffset(frame); +} + +#else /* if conversion not allowed */ + +/* Process raw YUV420 data into standard YUV420P */ +static void +ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + /* Deinterlace frame, if necessary */ + if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, frame->tempdata); + else + yuv420raw_to_yuv420p(frame, frame->rawdata, + frame->tempdata); + + deinterlace(frame, RAWFMT_YUV420, frame->tempdata, + frame->data); + } else { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, frame->data); + else + yuv420raw_to_yuv420p(frame, frame->rawdata, + frame->data); + } +} +#endif /* OV511_ALLOW_CONVERSION */ + +/* Post-processes the specified frame. This consists of: + * 1. Decompress frame, if necessary + * 2. Deinterlace frame and scale to proper size, if necessary + * 3. Convert from YUV planar to destination format, if necessary + * 4. Fix the RGB offset, if necessary + */ +static void +ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + if (dumppix) { + memset(frame->data, 0, + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); + PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd); + memcpy(frame->data, frame->rawdata, frame->bytes_recvd); + } else { + switch (frame->format) { + case VIDEO_PALETTE_GREY: + ov51x_postprocess_grey(ov, frame); + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: +#ifdef OV511_ALLOW_CONVERSION + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_YUV422P: +#endif + ov51x_postprocess_yuv420(ov, frame); + break; + default: + err("Cannot convert data to %s", + symbolic(v4l1_plist, frame->format)); + } + } +} + +/********************************************************************** + * + * OV51x data transfer, IRQ handler + * + **********************************************************************/ + +static inline void +ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n) +{ + int num, offset; + int pnum = in[ov->packet_size - 1]; /* Get packet number */ + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; + struct timeval *ts; + + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ + + if (printph) { + info("ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", + pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6], + in[7], in[8], in[9], in[10], in[11]); + } + + /* Check for SOF/EOF packet */ + if ((in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) || + (~in[8] & 0x08)) + goto check_middle; + + /* Frame end */ + if (in[8] & 0x80) { + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); + + /* Get the actual frame size from the EOF header */ + frame->rawwidth = ((int)(in[9]) + 1) * 8; + frame->rawheight = ((int)(in[10]) + 1) * 8; + + PDEBUG(4, "Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d", + ov->curframe, pnum, frame->rawwidth, frame->rawheight, + frame->bytes_recvd); + + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, + ov->maxheight); + + /* Don't allow byte count to exceed buffer size */ + RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); + + if (frame->scanstate == STATE_LINES) { + int nextf; + + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + } else { + if (ov->frame[nextf].grabstate == FRAME_DONE) { + PDEBUG(4, "No empty frames left"); + } else { + PDEBUG(4, "Frame not ready? state = %d", + ov->frame[nextf].grabstate); + } + + ov->curframe = -1; + } + } else { + PDEBUG(5, "Frame done, but not scanning"); + } + /* Image corruption caused by misplaced frame->segment = 0 + * fixed by carlosf@conectiva.com.br + */ + } else { + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov->curframe); + + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (in[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); + } + + frame->scanstate = STATE_LINES; + frame->bytes_recvd = 0; + frame->compressed = in[8] & 0x40; + } + +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(5, "Not in a frame; packet skipped"); + return; + } + + /* If frame start, skip header */ + if (frame->bytes_recvd == 0) + offset = 9; + else + offset = 0; + + num = n - offset - 1; + + /* Dump all data exactly as received */ + if (dumppix == 2) { + frame->bytes_recvd += n - 1; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - (n - 1), + in, n - 1); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else if (!frame->compressed && !remove_zeros) { + frame->bytes_recvd += num; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - num, + in + offset, num); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */ + int b, read = 0, allzero, copied = 0; + if (offset) { + frame->bytes_recvd += 32 - offset; // Bytes out + memcpy(frame->rawdata, in + offset, 32 - offset); + read += 32; + } + + while (read < n - 1) { + allzero = 1; + for (b = 0; b < 32; b++) { + if (in[read + b]) { + allzero = 0; + break; + } + } + + if (allzero) { + /* Don't copy it */ + } else { + if (frame->bytes_recvd + copied + 32 <= max_raw) + { + memcpy(frame->rawdata + + frame->bytes_recvd + copied, + in + read, 32); + copied += 32; + } else { + PDEBUG(3, "Raw data buffer overrun!!"); + } + } + read += 32; + } + + frame->bytes_recvd += copied; + } +} + +static inline void +ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) +{ + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; + struct timeval *ts; + + /* Don't copy the packet number byte */ + if (ov->packet_numbering) + --n; + + /* A false positive here is likely, until OVT gives me + * the definitive SOF/EOF format */ + if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) { + if (printph) { + info("ph: %2x %2x %2x %2x %2x %2x %2x %2x", in[0], + in[1], in[2], in[3], in[4], in[5], in[6], in[7]); + } + + if (frame->scanstate == STATE_LINES) { + PDEBUG(4, "Detected frame end/start"); + goto eof; + } else { //scanstate == STATE_SCANNING + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov->curframe); + goto sof; + } + } else { + goto check_middle; + } + +eof: + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); + + PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d", + ov->curframe, + (int)(in[9]), (int)(in[10]), frame->bytes_recvd); + + // FIXME: Since we don't know the header formats yet, + // there is no way to know what the actual image size is + frame->rawwidth = frame->width; + frame->rawheight = frame->height; + + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight); + + /* Don't allow byte count to exceed buffer size */ + RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); + + if (frame->scanstate == STATE_LINES) { + int nextf; + + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + frame = &ov->frame[nextf]; + } else { + if (ov->frame[nextf].grabstate == FRAME_DONE) { + PDEBUG(4, "No empty frames left"); + } else { + PDEBUG(4, "Frame not ready? state = %d", + ov->frame[nextf].grabstate); + } + + ov->curframe = -1; + PDEBUG(4, "SOF dropped (no active frame)"); + return; /* Nowhere to store this frame */ + } + } +sof: + PDEBUG(4, "Starting capture on frame %d", frame->framenum); + +// Snapshot not reverse-engineered yet. +#if 0 + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (in[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); + } +#endif + frame->scanstate = STATE_LINES; + frame->bytes_recvd = 0; + frame->compressed = 1; + +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(4, "scanstate: no SOF yet"); + return; + } + + /* Dump all data exactly as received */ + if (dumppix == 2) { + frame->bytes_recvd += n; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - n, in, n); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else { + /* All incoming data are divided into 8-byte segments. If the + * segment contains all zero bytes, it must be skipped. These + * zero-segments allow the OV518 to mainain a constant data rate + * regardless of the effectiveness of the compression. Segments + * are aligned relative to the beginning of each isochronous + * packet. The first segment in each image is a header (the + * decompressor skips it later). + */ + + int b, read = 0, allzero, copied = 0; + + while (read < n) { + allzero = 1; + for (b = 0; b < 8; b++) { + if (in[read + b]) { + allzero = 0; + break; + } + } + + if (allzero) { + /* Don't copy it */ + } else { + if (frame->bytes_recvd + copied + 8 <= max_raw) + { + memcpy(frame->rawdata + + frame->bytes_recvd + copied, + in + read, 8); + copied += 8; + } else { + PDEBUG(3, "Raw data buffer overrun!!"); + } + } + read += 8; + } + frame->bytes_recvd += copied; + } +} + +static inline void +ov519_move_data(struct usb_ov511 *ov, unsigned char *in, int n) +{ + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; + struct timeval *ts; + + /* Don't copy the packet number byte */ +// if (ov->packet_numbering) +// --n; +/* Header of ov519 is 16 bytes: + * Byte Value Description + * 0 0xff magic + * 1 0xff magic + * 2 0xff magic + * 3 0xXX 0x50 = SOF, 0x51 = EOF + * 9 0xXX 0x01 initial frame without data, 0x00 standard frame with image + * 14 Lo in EOF: length of image data / 8 + * 15 Hi + */ + + // Start Of Frame + if ((in[0]==0xff) && (in[1]==0xff) && (in[2]==0xff)) { + + if (printph) { + info("ph: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", + in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7], + in[8], in[9], in[10], in[11], in[12], in[13], in[14], in[15]); + } + + if (in[3]==0x50) { + PDEBUG(4, "Start Of Frame, framenum = %d", ov->curframe); + goto sof; + } else if (in[3]==0x51) { + PDEBUG(4, "End Of Frame"); + goto eof; + } else { + goto check_middle; + } + } else { + goto check_middle; + } + +eof: + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); + PDEBUG(4, "Frame end, curframe=%d, length=%d, recvd=%d", + ov->curframe, (int)((in[14]) + ((int)(in[15])<<8))<<3, frame->bytes_recvd - (jpeginfo?2:0)); + + + if (in[9]) { + PDEBUG(1, "initial frame"); + frame->scanstate = STATE_SCANNING; + return; + } + + // FIXME: Since we don't know the header formats yet, + // there is no way to know what the actual image size is + frame->rawwidth = frame->width; + frame->rawheight = frame->height; + + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight); + + /* Don't allow byte count to exceed buffer size */ + //RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); + + if (frame->scanstate == STATE_LINES) { + int nextf; + + if (((int)((in[14]) + ((int)(in[15])<<8))<<3) != (frame->bytes_recvd - (jpeginfo?2:0))) { + info("Data length in header and number of received bytes differ"); + frame->scanstate = STATE_SCANNING; + return; + } + + if (jpeginfo) { + frame->data[0] = in[14]; + frame->data[1] = in[15]; + } + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + } else { + ov->curframe = -1; + } + } else { + info("EOF without SOF"); // This happens if there was no active frame when SOF arrived + } + return; + +sof: + PDEBUG(4, "Starting capture on frame %d", frame->framenum); + + // Skip SOF Header: + in += 16; + n -= 16; + + frame->scanstate = STATE_LINES; + if (jpeginfo) { + frame->bytes_recvd = 2; // Space for length bytes. Will be written at EOF + frame->data[0] = 0; + frame->data[1] = 0; + } else { + frame->bytes_recvd = 0; + } + frame->compressed = 1; + +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(4, "scanstate: no SOF yet"); + return; + } + + /* Dump all data exactly as received. It is standard JPEG */ + frame->bytes_recvd += n; + if (frame->bytes_recvd <= max_raw) { + memcpy(frame->data + frame->bytes_recvd - n, in, n); + } else { + PDEBUG(3, "Raw data buffer overrun!! (%d)", frame->bytes_recvd - max_raw); + } +} + +static void +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 51) +ov51x_isoc_irq(struct urb *urb, struct pt_regs *regs) +#else +ov51x_isoc_irq(struct urb *urb) +#endif +{ + int i; + struct usb_ov511 *ov; + struct ov511_sbuf *sbuf; + + if (!urb->context) { + PDEBUG(4, "no context"); + return; + } + + sbuf = urb->context; + ov = sbuf->ov; + + if (!ov || !ov->dev || !ov->user) { + PDEBUG(4, "no device, or not open"); + return; + } + + if (!ov->streaming) { + PDEBUG(4, "hmmm... not streaming, but got interrupt"); + return; + } + + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + PDEBUG(4, "URB unlinked"); + return; + } + + if (urb->status != -EINPROGRESS && urb->status != 0) { + err("ERROR: urb->status=%d: %s", urb->status, + symbolic(urb_errlist, urb->status)); + } + + /* Copy the data received into our frame buffer */ + PDEBUG(5, "sbuf[%d]: Moving %d packets", sbuf->n, + urb->number_of_packets); + for (i = 0; i < urb->number_of_packets; i++) { + /* Warning: Don't call *_move_data() if no frame active! */ + if (ov->curframe >= 0) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + unsigned char *cdata; + + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = 0; + + cdata = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!n) { + PDEBUG(4, "Zero-length packet"); + continue; + } + + if (st) + PDEBUG(2, "data error: [%d] len=%d, status=%d", + i, n, st); + + switch (ov->bclass) { + case BCL_OV511: + ov511_move_data(ov, cdata, n); + break; + case BCL_OV518: + ov518_move_data(ov, cdata, n); + break; + case BCL_OV519: + ov519_move_data(ov, cdata, n); + break; + default: + err("Unknown bridge device (%d)", ov->bridge); + } + } else if (waitqueue_active(&ov->wq)) { + wake_up_interruptible(&ov->wq); + } + } + + /* Resubmit this URB */ + urb->dev = ov->dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 4) + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) +#else + if ((i = usb_submit_urb(urb)) != 0) +#endif + err("usb_submit_urb() ret %d", i); + + return; +} + +/**************************************************************************** + * + * Stream initialization and termination + * + ***************************************************************************/ + +static int +ov51x_init_isoc(struct usb_ov511 *ov) +{ + struct urb *urb; + int fx, err, n, size; + + PDEBUG(3, "*** Initializing capture ***"); + + ov->curframe = -1; + + switch (ov->bridge) { + case BRG_OV511: + if (cams == 1) size = 993; + else if (cams == 2) size = 513; + else if (cams == 3 || cams == 4) size = 257; + else { + err("\"cams\" parameter too high!"); + return -1; + } + break; + case BRG_OV511PLUS: + if (cams == 1) size = 961; + else if (cams == 2) size = 513; + else if (cams == 3 || cams == 4) size = 257; + else if (cams >= 5 && cams <= 8) size = 129; + else if (cams >= 9 && cams <= 31) size = 33; + else { + err("\"cams\" parameter too high!"); + return -1; + } + break; + case BRG_OV518: + case BRG_OV518PLUS: + if (cams == 1) size = 896; + else if (cams == 2) size = 512; + else if (cams == 3 || cams == 4) size = 256; + else if (cams >= 5 && cams <= 8) size = 128; + else { + err("\"cams\" parameter too high!"); + return -1; + } + break; + case BRG_OV519: + if (cams == 1) size = 896; + else if (cams == 2) size = 512; + else { + err("\"cams\" parameter too high!"); + return -1; + } + break; + default: + err("invalid bridge type"); + return -1; + } + + // FIXME: OV518+ is hardcoded to 15 FPS (alternate 5) for now + if (ov->bridge == BRG_OV518PLUS) { + if (packetsize == -1) { + ov518_set_packet_size(ov, 640); + } else { + info("Forcing packet size to %d", packetsize); + ov518_set_packet_size(ov, packetsize); + } + } else if (ov->bridge == BRG_OV518) { + if (packetsize == -1) { + ov518_set_packet_size(ov, 896); + } else { + info("Forcing packet size to %d", packetsize); + ov518_set_packet_size(ov, packetsize); + } + } else if (ov->bridge == BRG_OV519) { + if (packetsize == -1) { + ov519_set_packet_size(ov, size); + } else { + info("Forcing packet size to %d", packetsize); + ov519_set_packet_size(ov, packetsize); + } + } else { + if (packetsize == -1) { + ov511_set_packet_size(ov, size); + } else { + info("Forcing packet size to %d", packetsize); + ov511_set_packet_size(ov, packetsize); + } + } + + for (n = 0; n < OV511_NUMSBUF; n++) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 5) + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); +#else + urb = usb_alloc_urb(FRAMES_PER_DESC); +#endif + if (!urb) { + err("init isoc: usb_alloc_urb ret. NULL"); + return -ENOMEM; + } + ov->sbuf[n].urb = urb; + urb->dev = ov->dev; + urb->context = &ov->sbuf[n]; + urb->pipe = usb_rcvisocpipe(ov->dev, OV511_ENDPOINT_ADDRESS); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 45) + urb->transfer_flags = URB_ISO_ASAP; +#else + urb->transfer_flags = USB_ISO_ASAP; +#endif + urb->transfer_buffer = ov->sbuf[n].data; + urb->complete = ov51x_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC; + urb->interval = 1; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = ov->packet_size * fx; + urb->iso_frame_desc[fx].length = ov->packet_size; + } + } + + ov->streaming = 1; + + for (n = 0; n < OV511_NUMSBUF; n++) { + ov->sbuf[n].urb->dev = ov->dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 4) + err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL); +#else + err = usb_submit_urb(ov->sbuf[n].urb); +#endif + if (err) { + err("init isoc: usb_submit_urb(%d) ret %d", n, err); + return err; + } + } + + return 0; +} + +static void +ov51x_unlink_isoc(struct usb_ov511 *ov) +{ + int n; + + /* Unschedule all of the iso td's */ + for (n = OV511_NUMSBUF - 1; n >= 0; n--) { + if (ov->sbuf[n].urb) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) + usb_unlink_urb(ov->sbuf[n].urb); +#else + usb_kill_urb(ov->sbuf[n].urb); +#endif + usb_free_urb(ov->sbuf[n].urb); + ov->sbuf[n].urb = NULL; + } + } +} + +static void +ov51x_stop_isoc(struct usb_ov511 *ov) +{ + if (!ov->streaming || !ov->dev) + return; + + PDEBUG(3, "*** Stopping capture ***"); + + switch (ov->bclass) { + case BCL_OV511: + ov511_set_packet_size(ov, 0); + break; + case BCL_OV518: + ov518_set_packet_size(ov, 0); + break; + case BCL_OV519: + ov519_set_packet_size(ov, 0); + break; + } + ov->streaming = 0; + + ov51x_unlink_isoc(ov); +} + +static int +ov51x_new_frame(struct usb_ov511 *ov, int framenum) +{ + struct ov511_frame *frame; + int newnum; + + PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum); + + if (!ov->dev) + return -1; + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (ov->curframe == -1) { + newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES; + if (ov->frame[newnum].grabstate == FRAME_READY) + framenum = newnum; + } else + return 0; + + frame = &ov->frame[framenum]; + + PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, + frame->width, frame->height); + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + frame->snapshot = 0; + + ov->curframe = framenum; + + /* Make sure it's not too big */ + if (frame->width > ov->maxwidth) + frame->width = ov->maxwidth; + + frame->width &= ~7L; /* Multiple of 8 */ + + if (frame->height > ov->maxheight) + frame->height = ov->maxheight; + + frame->height &= ~3L; /* Multiple of 4 */ + + return 0; +} + +/**************************************************************************** + * + * Buffer management + * + ***************************************************************************/ + +/* + * - You must acquire buf_lock before entering this function. + * - Because this code will free any non-null pointer, you must be sure to null + * them if you explicitly free them somewhere else! + */ +static void +ov51x_do_dealloc(struct usb_ov511 *ov) +{ + int i; + PDEBUG(4, "entered"); + + if (ov->fbuf) { + rvfree(ov->fbuf, OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); + ov->fbuf = NULL; + } + + vfree(ov->rawfbuf); + ov->rawfbuf = NULL; + + vfree(ov->tempfbuf); + ov->tempfbuf = NULL; + + for (i = 0; i < OV511_NUMSBUF; i++) { + kfree(ov->sbuf[i].data); + ov->sbuf[i].data = NULL; + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].data = NULL; + ov->frame[i].rawdata = NULL; + ov->frame[i].tempdata = NULL; + if (ov->frame[i].compbuf) { + free_page((unsigned long) ov->frame[i].compbuf); + ov->frame[i].compbuf = NULL; + } + } + + PDEBUG(4, "buffer memory deallocated"); + ov->buf_state = BUF_NOT_ALLOCATED; + PDEBUG(4, "leaving"); +} + +static int +ov51x_alloc(struct usb_ov511 *ov) +{ + int i; + const int w = ov->maxwidth; + const int h = ov->maxheight; + const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE(w, h); + const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h); + + PDEBUG(4, "entered"); + down(&ov->buf_lock); + + if (ov->buf_state == BUF_ALLOCATED) + goto out; + + ov->fbuf = rvmalloc(data_bufsize); + if (!ov->fbuf) + goto error; + + ov->rawfbuf = vmalloc(raw_bufsize); + if (!ov->rawfbuf) + goto error; + + memset(ov->rawfbuf, 0, raw_bufsize); + + ov->tempfbuf = vmalloc(raw_bufsize); + if (!ov->tempfbuf) + goto error; + + memset(ov->tempfbuf, 0, raw_bufsize); + + for (i = 0; i < OV511_NUMSBUF; i++) { + ov->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov->sbuf[i].data) + goto error; + + PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data); + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h); + ov->frame[i].rawdata = ov->rawfbuf + + i * MAX_RAW_DATA_SIZE(w, h); + ov->frame[i].tempdata = ov->tempfbuf + + i * MAX_RAW_DATA_SIZE(w, h); + + ov->frame[i].compbuf = + (unsigned char *) __get_free_page(GFP_KERNEL); + if (!ov->frame[i].compbuf) + goto error; + + PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data); + } + + ov->buf_state = BUF_ALLOCATED; +out: + up(&ov->buf_lock); + PDEBUG(4, "leaving"); + return 0; +error: + ov51x_do_dealloc(ov); + up(&ov->buf_lock); + PDEBUG(4, "errored"); + return -ENOMEM; +} + +static void +ov51x_dealloc(struct usb_ov511 *ov) +{ + PDEBUG(4, "entered"); + down(&ov->buf_lock); + ov51x_do_dealloc(ov); + up(&ov->buf_lock); + PDEBUG(4, "leaving"); +} + +/**************************************************************************** + * + * V4L 1 API + * + ***************************************************************************/ + +#ifdef OV511_OLD_V4L +static int +ov51x_v4l1_open(struct video_device *vdev, int flags) +{ +#else +static int +ov51x_v4l1_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); +#endif + struct usb_ov511 *ov = video_get_drvdata(vdev); + int err, i; + +/* 2.2.x needs explicit module-locking */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 17) + MOD_INC_USE_COUNT; +#endif + + PDEBUG(4, "opening"); + + down(&ov->lock); + + err = -EBUSY; + if (ov->user) + goto out; + + ov->sub_flag = 0; + + /* In case app doesn't set them... */ + err = ov51x_set_default_params(ov); + if (err < 0) + goto out; + + /* Make sure frames are reset */ + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].grabstate = FRAME_UNUSED; + ov->frame[i].bytes_read = 0; + } + + /* If compression is on, make sure now that a + * decompressor can be loaded */ + if (ov->compress && !ov->decomp_ops) { + err = request_decompressor(ov); + if (err && !dumppix) + goto out; + } + + err = ov51x_alloc(ov); + if (err < 0) + goto out; + + err = ov51x_init_isoc(ov); + if (err) { + ov51x_dealloc(ov); + goto out; + } + + ov->user++; +// If using _NEW_ V4L... +#if !defined(OV511_OLD_V4L) + file->private_data = vdev; +#endif + + if (ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 1); + +out: + up(&ov->lock); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 17) + if (err) + MOD_DEC_USE_COUNT; +#endif + + return err; +} + +#ifdef OV511_OLD_V4L +static void +ov51x_v4l1_close(struct video_device *vdev) +{ +#else +static int +ov51x_v4l1_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = file->private_data; +#endif + struct usb_ov511 *ov = video_get_drvdata(vdev); + + PDEBUG(4, "ov511_close"); + + down(&ov->lock); + + ov->user--; + ov51x_stop_isoc(ov); + + release_decompressor(ov); + + if (ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); + + if (ov->dev) + ov51x_dealloc(ov); + + up(&ov->lock); + + /* Device unplugged while open. Only a minimum of unregistration is done + * here; the disconnect callback already did the rest. */ + if (!ov->dev) { + down(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + up(&ov->cbuf_lock); + + ov51x_dealloc(ov); +#ifdef OV511_OLD_V4L + video_unregister_device(&ov->vdev); +#endif + kfree(ov); + ov = NULL; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 17) + MOD_DEC_USE_COUNT; +#endif + +#ifdef OV511_OLD_V4L + return; +#else + file->private_data = NULL; + return 0; +#endif +} + +/* Do not call this function directly! */ +static int +#ifdef OV511_OLD_V4L +ov51x_v4l1_ioctl_internal(struct usb_ov511 *ov, unsigned int cmd, + void *arg) +{ +#else +ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct usb_ov511 *ov = video_get_drvdata(vdev); +#endif + PDEBUG(5, "IOCtl: 0x%X", cmd); + + if (!ov->dev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + + PDEBUG(4, "VIDIOCGCAP"); + + memset(b, 0, sizeof(struct video_capability)); + sprintf(b->name, "%s USB Camera", + symbolic(brglist, ov->bridge)); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b->channels = ov->num_inputs; + b->audios = 0; + b->maxwidth = ov->maxwidth; + b->maxheight = ov->maxheight; + b->minwidth = ov->minwidth; + b->minheight = ov->minheight; + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + PDEBUG(4, "VIDIOCGCHAN"); + + if ((unsigned)(v->channel) >= ov->num_inputs) { + err("Invalid channel (%d)", v->channel); + return -EINVAL; + } + + v->norm = ov->norm; + v->type = VIDEO_TYPE_CAMERA; + v->flags = 0; +// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0; + v->tuners = 0; + decoder_get_input_name(ov, v->channel, v->name); + + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + int err; + + PDEBUG(4, "VIDIOCSCHAN"); + + /* Make sure it's not a camera */ + if (!ov->has_decoder) { + if (v->channel == 0) + return 0; + else + return -EINVAL; + } + + if (v->norm != VIDEO_MODE_PAL && + v->norm != VIDEO_MODE_NTSC && + v->norm != VIDEO_MODE_SECAM && + v->norm != VIDEO_MODE_AUTO) { + err("Invalid norm (%d)", v->norm); + return -EINVAL; + } + + if ((unsigned)(v->channel) >= ov->num_inputs) { + err("Invalid channel (%d)", v->channel); + return -EINVAL; + } + + err = decoder_set_input(ov, v->channel); + if (err) + return err; + + err = decoder_set_norm(ov, v->norm); + if (err) + return err; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + + PDEBUG(4, "VIDIOCGPICT"); + + memset(p, 0, sizeof(struct video_picture)); + if (sensor_get_picture(ov, p)) + return -EIO; + + /* Can we get these from frame[0]? -claudio? */ + p->depth = ov->frame[0].depth; + p->palette = ov->frame[0].format; + + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + int i, rc; + + PDEBUG(4, "VIDIOCSPICT"); + + if (!get_depth(p->palette)) + return -EINVAL; + + if (sensor_set_picture(ov, p)) + return -EIO; + + if (force_palette && p->palette != force_palette) { + info("Palette rejected (%s)", + symbolic(v4l1_plist, p->palette)); + return -EINVAL; + } + + // FIXME: Format should be independent of frames + if (p->palette != ov->frame[0].format) { + PDEBUG(4, "Detected format change"); + + rc = ov51x_wait_frames_inactive(ov); + if (rc) + return rc; + + mode_init_regs(ov, ov->frame[0].width, + ov->frame[0].height, p->palette, ov->sub_flag); + } + + PDEBUG(4, "Setting depth=%d, palette=%s", + p->depth, symbolic(v4l1_plist, p->palette)); + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].depth = p->depth; + ov->frame[i].format = p->palette; + } + + return 0; + } + case VIDIOCGCAPTURE: + { + int *vf = arg; + + PDEBUG(4, "VIDIOCGCAPTURE"); + + ov->sub_flag = *vf; + return 0; + } + case VIDIOCSCAPTURE: + { + struct video_capture *vc = arg; + + PDEBUG(4, "VIDIOCSCAPTURE"); + + if (vc->flags) + return -EINVAL; + if (vc->decimation) + return -EINVAL; + + vc->x &= ~3L; + vc->y &= ~1L; + vc->y &= ~31L; + + if (vc->width == 0) + vc->width = 32; + + vc->height /= 16; + vc->height *= 16; + if (vc->height == 0) + vc->height = 16; + + ov->subx = vc->x; + ov->suby = vc->y; + ov->subw = vc->width; + ov->subh = vc->height; + + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int i, rc; + + PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height); + +#if 0 + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->height != ov->maxheight) + return -EINVAL; + if (vw->width != ov->maxwidth) + return -EINVAL; +#endif + + rc = ov51x_wait_frames_inactive(ov); + if (rc) + return rc; + + rc = mode_init_regs(ov, vw->width, vw->height, + ov->frame[0].format, ov->sub_flag); + if (rc < 0) + return rc; + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].width = vw->width; + ov->frame[i].height = vw->height; + } + + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + memset(vw, 0, sizeof(struct video_window)); + vw->x = 0; /* FIXME */ + vw->y = 0; + vw->width = ov->frame[0].width; + vw->height = ov->frame[0].height; + vw->flags = 30; + + PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height); + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + PDEBUG(4, "VIDIOCGMBUF"); + + memset(vm, 0, sizeof(struct video_mbuf)); + vm->size = OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); + vm->frames = OV511_NUMFRAMES; + + vm->offsets[0] = 0; + for (i = 1; i < OV511_NUMFRAMES; i++) { + vm->offsets[i] = vm->offsets[i-1] + + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); + } + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + int rc, depth; + unsigned int f = vm->frame; + + PDEBUG(4, "VIDIOCMCAPTURE: frame: %d, %dx%d, %s", f, vm->width, + vm->height, symbolic(v4l1_plist, vm->format)); + + depth = get_depth(vm->format); + if (!depth) { + PDEBUG(2, "VIDIOCMCAPTURE: invalid format (%s)", + symbolic(v4l1_plist, vm->format)); + return -EINVAL; + } + + if (f >= OV511_NUMFRAMES) { + err("VIDIOCMCAPTURE: invalid frame (%d)", f); + return -EINVAL; + } + + if (vm->width > ov->maxwidth + || vm->height > ov->maxheight) { + err("VIDIOCMCAPTURE: requested dimensions too big"); + return -EINVAL; + } + + if (ov->frame[f].grabstate == FRAME_GRABBING) { + PDEBUG(4, "VIDIOCMCAPTURE: already grabbing"); + return -EBUSY; + } + + if (force_palette && (vm->format != force_palette)) { + PDEBUG(2, "palette rejected (%s)", + symbolic(v4l1_plist, vm->format)); + return -EINVAL; + } + + if ((ov->frame[f].width != vm->width) || + (ov->frame[f].height != vm->height) || + (ov->frame[f].format != vm->format) || + (ov->frame[f].sub_flag != ov->sub_flag) || + (ov->frame[f].depth != depth)) { + PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters"); + + rc = ov51x_wait_frames_inactive(ov); + if (rc) + return rc; + + rc = mode_init_regs(ov, vm->width, vm->height, + vm->format, ov->sub_flag); +#if 0 + if (rc < 0) { + PDEBUG(1, "Got error while initializing regs "); + return ret; + } +#endif + ov->frame[f].width = vm->width; + ov->frame[f].height = vm->height; + ov->frame[f].format = vm->format; + ov->frame[f].sub_flag = ov->sub_flag; + ov->frame[f].depth = depth; + } + + /* Mark it as ready */ + ov->frame[f].grabstate = FRAME_READY; + + PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", f); + + return ov51x_new_frame(ov, f); + } + case VIDIOCSYNC: + { + unsigned int fnum = *((unsigned int *) arg); + struct ov511_frame *frame; + int rc; + + if (fnum >= OV511_NUMFRAMES) { + err("VIDIOCSYNC: invalid frame (%d)", fnum); + return -EINVAL; + } + + frame = &ov->frame[fnum]; + + PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum, + frame->grabstate); + + switch (frame->grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: +redo: + if (!ov->dev) + return -EIO; + + rc = wait_event_interruptible(frame->wq, + (frame->grabstate == FRAME_DONE) + || (frame->grabstate == FRAME_ERROR)); + + if (rc) + return rc; + + if (frame->grabstate == FRAME_ERROR) { + if ((rc = ov51x_new_frame(ov, fnum)) < 0) + return rc; + goto redo; + } + /* Fall through */ + case FRAME_DONE: + if (ov->snap_enabled && !frame->snapshot) { + if ((rc = ov51x_new_frame(ov, fnum)) < 0) + return rc; + goto redo; + } + + frame->grabstate = FRAME_UNUSED; + + /* Reset the hardware snapshot button */ + /* FIXME - Is this the best place for this? */ + if ((ov->snap_enabled) && (frame->snapshot)) { + frame->snapshot = 0; + ov51x_clear_snapshot(ov); + } + + /* Decompression, format conversion, etc... */ + if (ov->bclass != BCL_OV519) { + ov51x_postprocess(ov, frame); + } + + break; + } /* end switch */ + + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + PDEBUG(4, "VIDIOCGFBUF"); + + memset(vb, 0, sizeof(struct video_buffer)); + + return 0; + } + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + PDEBUG(4, "VIDIOCGUNIT"); + + memset(vu, 0, sizeof(struct video_unit)); + + vu->video = ov->vdev->minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; + + return 0; + } + case OV511IOC_WI2C: + { + struct ov511_i2c_struct *w = arg; + + return i2c_w_slave(ov, w->slave, w->reg, w->value, w->mask); + } + case OV511IOC_RI2C: + { + struct ov511_i2c_struct *r = arg; + int rc; + + rc = i2c_r_slave(ov, r->slave, r->reg); + if (rc < 0) + return rc; + + r->value = rc; + return 0; + } + default: + PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd); + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +#ifdef OV511_OLD_V4L +/* This is implemented as video_generic_ioctl() in the new V4L's videodev.c */ +static int +ov51x_v4l1_generic_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + err = ov51x_v4l1_ioctl_internal(vdev->priv, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user(arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + if (mbuf) + kfree(mbuf); + return err; +} + +static int +ov51x_v4l1_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + struct usb_ov511 *ov = vdev->priv; + int rc; + + if (down_interruptible(&ov->lock)) + return -EINTR; + + rc = ov51x_v4l1_generic_ioctl(vdev, cmd, arg); + + up(&ov->lock); + return rc; +} + +#else /* If new V4L API */ + +static int +ov51x_v4l1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = file->private_data; + struct usb_ov511 *ov = video_get_drvdata(vdev); + int rc; + + if (down_interruptible(&ov->lock)) + return -EINTR; + + rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal); + + up(&ov->lock); + return rc; +} +#endif /* OV511_OLD_V4L */ + +#ifdef OV511_OLD_V4L +static long +ov51x_v4l1_read(struct video_device *vdev, char *buf, unsigned long count, + int noblock) +{ +#else +static ssize_t +ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos) +{ + struct video_device *vdev = file->private_data; + int noblock = file->f_flags&O_NONBLOCK; + unsigned long count = cnt; +#endif + struct usb_ov511 *ov = video_get_drvdata(vdev); + int i, rc = 0, frmx = -1; + struct ov511_frame *frame; + + if (down_interruptible(&ov->lock)) + return -EINTR; + + PDEBUG(4, "%ld bytes, noblock=%d", count, noblock); + + if (!vdev || !buf) { + rc = -EFAULT; + goto error; + } + + if (!ov->dev) { + rc = -EIO; + goto error; + } + +// FIXME: Only supports two frames + /* See if a frame is completed, then use it. */ + if (ov->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + frmx = 0; + else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ + frmx = 1; + + /* If nonblocking we return immediately */ + if (noblock && (frmx == -1)) { + rc = -EAGAIN; + goto error; + } + + /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ + /* See if a frame is in process (grabbing), then use it. */ + if (frmx == -1) { + if (ov->frame[0].grabstate == FRAME_GRABBING) + frmx = 0; + else if (ov->frame[1].grabstate == FRAME_GRABBING) + frmx = 1; + } + + /* If no frame is active, start one. */ + if (frmx == -1) { + if ((rc = ov51x_new_frame(ov, frmx = 0))) { + err("read: ov51x_new_frame error"); + goto error; + } + } + + frame = &ov->frame[frmx]; + +restart: + if (!ov->dev) { + rc = -EIO; + goto error; + } + + /* Wait while we're grabbing the image */ + PDEBUG(4, "Waiting image grabbing"); + rc = wait_event_interruptible(frame->wq, + (frame->grabstate == FRAME_DONE) + || (frame->grabstate == FRAME_ERROR)); + + if (rc) + goto error; + + PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate); + PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd); + + if (frame->grabstate == FRAME_ERROR) { + frame->bytes_read = 0; + err("** ick! ** Errored frame %d", ov->curframe); + if (ov51x_new_frame(ov, frmx)) { + err("read: ov51x_new_frame error"); + goto error; + } + goto restart; + } + + + /* Repeat until we get a snapshot frame */ + if (ov->snap_enabled) + PDEBUG(4, "Waiting snapshot frame"); + if (ov->snap_enabled && !frame->snapshot) { + frame->bytes_read = 0; + if ((rc = ov51x_new_frame(ov, frmx))) { + err("read: ov51x_new_frame error"); + goto error; + } + goto restart; + } + + /* Clear the snapshot */ + if (ov->snap_enabled && frame->snapshot) { + frame->snapshot = 0; + ov51x_clear_snapshot(ov); + } + + /* Decompression, format conversion, etc... */ + if (ov->bclass != BCL_OV519) { + ov51x_postprocess(ov, frame); + } + + PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx, + frame->bytes_read, + get_frame_length(ov, frame)); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > get_frame_length(ov, (struct ov511_frame *)frame)) { + count = frame->bytes_recvd - frame->bytes_read; + PDEBUG(4, "set count to %d", (int)count); + } + + /* FIXME - count hardwired to be one frame... */ + //count = get_frame_length(ov, frame); + + PDEBUG(4, "Copy to user space: %ld bytes", count); + if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) { + PDEBUG(4, "Copy failed! %d bytes not copied", i); + rc = -EFAULT; + goto error; + } + + frame->bytes_read += count; + PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld", + count, frame->bytes_read); + + /* If all data have been read... */ + if (frame->bytes_read >= get_frame_length(ov, frame)) { + frame->bytes_read = 0; + +// FIXME: Only supports two frames + /* Mark it as available to be used again. */ + ov->frame[frmx].grabstate = FRAME_UNUSED; + if ((rc = ov51x_new_frame(ov, !frmx))) { + err("ov51x_new_frame returned error"); + goto error; + } + } + + PDEBUG(4, "read finished, returning %ld (sweet)", count); + + up(&ov->lock); + return count; + +error: + up(&ov->lock); + return rc; +} + +static int +#ifdef OV511_OLD_V4L + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) +ov51x_v4l1_mmap(struct vm_area_struct *vma, struct video_device *vdev, + const char *adr, unsigned long size) + #else +ov51x_v4l1_mmap(struct video_device *vdev, const char *adr, unsigned long size) + #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) */ +{ + unsigned long start = (unsigned long)adr; + +#else /* New V4L API */ + +ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; +#endif /* OV511_OLD_V4L */ + + struct usb_ov511 *ov = video_get_drvdata(vdev); + unsigned long page, pos; + + if (ov->dev == NULL) + return -EIO; + + PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); + + if (size > (((OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight) + + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))) + return -EINVAL; + + if (down_interruptible(&ov->lock)) + return -EINTR; + + pos = (unsigned long)ov->fbuf; + while (size > 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) || defined(RH9_REMAP) + page = kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, + PAGE_SHARED)) { +#else + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { +#endif + up(&ov->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + up(&ov->lock); + return 0; +} + +#ifdef OV511_OLD_V4L +static struct video_device vdev_template = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 17) + .owner = THIS_MODULE, +#endif + .name = "OV511 USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_OV511, + .open = ov51x_v4l1_open, + .close = ov51x_v4l1_close, + .read = ov51x_v4l1_read, + .ioctl = ov51x_v4l1_ioctl, + .mmap = ov51x_v4l1_mmap, +}; + +#else /* New V4L API */ + +static struct file_operations ov511_fops = { + .owner = THIS_MODULE, + .open = ov51x_v4l1_open, + .release = ov51x_v4l1_close, + .read = ov51x_v4l1_read, + .mmap = ov51x_v4l1_mmap, + .ioctl = ov51x_v4l1_ioctl, + .llseek = no_llseek, +}; + +static struct video_device vdev_template = { + .owner = THIS_MODULE, + .name = "OV51x USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_OV511, + .fops = &ov511_fops, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + .release = video_device_release, +#endif + .minor = -1, +}; +#endif /* OV511_OLD_V4L */ + +#if defined(CONFIG_VIDEO_PROC_FS) +static int +ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long ularg) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 5) + struct proc_dir_entry *pde = PDE(inode); +#else + struct proc_dir_entry *pde = inode->u.generic_ip; +#endif + struct usb_ov511 *ov; + void *arg = (void *) ularg; + int rc; + + if (!pde) + return -ENOENT; + + ov = pde->data; + if (!ov) + return -ENODEV; + + if (!ov->dev) + return -EIO; + + /* Should we pass through standard V4L IOCTLs? */ + + switch (cmd) { + case OV511IOC_GINTVER: + { + return OV511_INTERFACE_VER; + } + case OV511IOC_GUSHORT: + { + struct ov511_ushort_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_USOPT_BRIGHT: + rc = sensor_get_brightness(ov, &(opt.val)); + if (rc) return rc; + break; + case OV511_USOPT_SAT: + rc = sensor_get_saturation(ov, &(opt.val)); + if (rc) return rc; + break; + case OV511_USOPT_HUE: + rc = sensor_get_hue(ov, &(opt.val)); + if (rc) return rc; + break; + case OV511_USOPT_CONTRAST: + rc = sensor_get_contrast(ov, &(opt.val)); + if (rc) return rc; + break; + default: + err("Invalid get short option number"); + return -EINVAL; + } + + if (copy_to_user(arg, &opt, sizeof(opt))) + return -EFAULT; + + return 0; + } + case OV511IOC_SUSHORT: + { + struct ov511_ushort_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_USOPT_BRIGHT: + rc = sensor_set_brightness(ov, opt.val); + if (rc) return rc; + break; + case OV511_USOPT_SAT: + rc = sensor_set_saturation(ov, opt.val); + if (rc) return rc; + break; + case OV511_USOPT_HUE: + rc = sensor_set_hue(ov, opt.val); + if (rc) return rc; + break; + case OV511_USOPT_CONTRAST: + rc = sensor_set_contrast(ov, opt.val); + if (rc) return rc; + break; + default: + err("Invalid set short option number"); + return -EINVAL; + } + + return 0; + } + case OV511IOC_GUINT: + { + struct ov511_uint_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_UIOPT_POWER_FREQ: + opt.val = ov->lightfreq; + break; + case OV511_UIOPT_BFILTER: + opt.val = ov->bandfilt; + break; + case OV511_UIOPT_LED: + opt.val = ov->led_policy; + break; + case OV511_UIOPT_LED2: + opt.val = ov->led2_policy; + break; + case OV511_UIOPT_DEBUG: + opt.val = debug; + break; + case OV511_UIOPT_COMPRESS: + opt.val = ov->compress; + break; + default: + err("Invalid get int option number"); + return -EINVAL; + } + + if (copy_to_user(arg, &opt, sizeof(opt))) + return -EFAULT; + + return 0; + } + case OV511IOC_SUINT: + { + struct ov511_uint_opt opt; + + if (copy_from_user(&opt, arg, sizeof(opt))) + return -EFAULT; + + switch (opt.optnum) { + case OV511_UIOPT_POWER_FREQ: + rc = sensor_set_light_freq(ov, opt.val); + if (rc) return rc; + break; + case OV511_UIOPT_BFILTER: + rc = sensor_set_banding_filter(ov, opt.val); + if (rc) return rc; + break; + case OV511_UIOPT_LED: + if (opt.val <= 2) { + ov->led_policy = opt.val; + if (ov->led_policy == LED_OFF) + ov51x_led_control(ov, 0); + else if (ov->led_policy == LED_ON) + ov51x_led_control(ov, 1); + } else { + return -EINVAL; + } + break; + case OV511_UIOPT_LED2: + if (opt.val <= 2) { + ov->led2_policy = opt.val; + if (ov->led2_policy == LED_OFF) + ov51x_led_control(ov, 0); + else if (ov->led2_policy == LED_ON) + ov51x_led_control(ov, 1); + } else { + return -EINVAL; + } + break; + case OV511_UIOPT_DEBUG: + if (opt.val <= 5) + debug = opt.val; + else + return -EINVAL; + break; + case OV511_UIOPT_COMPRESS: + ov->compress = opt.val; + if (ov->compress) { + if (ov->bclass == BCL_OV511) + ov511_init_compression(ov); + else if (ov->bclass == BCL_OV518) + ov518_init_compression(ov); + else if (ov->bclass == BCL_OV519) + ov519_init_compression(ov); + } + break; + default: + err("Invalid get int option number"); + return -EINVAL; + } + + return 0; + } + case OV511IOC_WI2C: + { + struct ov511_i2c_struct w; + + if (copy_from_user(&w, arg, sizeof(w))) + return -EFAULT; + + return i2c_w_slave(ov, w.slave, w.reg, w.value, w.mask); + } + case OV511IOC_RI2C: + { + struct ov511_i2c_struct r; + + if (copy_from_user(&r, arg, sizeof(r))) + return -EFAULT; + + rc = i2c_r_slave(ov, r.slave, r.reg); + if (rc < 0) + return rc; + + r.value = rc; + + if (copy_to_user(arg, &r, sizeof(r))) + return -EFAULT; + + return 0; + } + default: + return -EINVAL; + } + + return 0; +} +#endif + +/**************************************************************************** + * + * OV511 and sensor configuration + * + ***************************************************************************/ + +/* This initializes the OV8110, OV8610 sensor. The OV8110 uses + * the same register settings as the OV8610, since they are very similar. + */ +static int +ov8xx0_configure(struct usb_ov511 *ov) +{ + int rc; + + static struct ov511_regvals regvals_norm_8610[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, + { OV511_I2C_BUS, 0x00, 0x00 }, + { OV511_I2C_BUS, 0x01, 0x80 }, + { OV511_I2C_BUS, 0x02, 0x80 }, + { OV511_I2C_BUS, 0x03, 0xc0 }, + { OV511_I2C_BUS, 0x04, 0x30 }, + { OV511_I2C_BUS, 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { OV511_I2C_BUS, 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { OV511_I2C_BUS, 0x0a, 0x86 }, + { OV511_I2C_BUS, 0x0b, 0xb0 }, + { OV511_I2C_BUS, 0x0c, 0x20 }, + { OV511_I2C_BUS, 0x0d, 0x20 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x12, 0x25 }, + { OV511_I2C_BUS, 0x13, 0x01 }, + { OV511_I2C_BUS, 0x14, 0x04 }, + { OV511_I2C_BUS, 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { OV511_I2C_BUS, 0x16, 0x03 }, + { OV511_I2C_BUS, 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { OV511_I2C_BUS, 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { OV511_I2C_BUS, 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { OV511_I2C_BUS, 0x1a, 0xf5 }, + { OV511_I2C_BUS, 0x1b, 0x00 }, + { OV511_I2C_BUS, 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { OV511_I2C_BUS, 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { OV511_I2C_BUS, 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { OV511_I2C_BUS, 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { OV511_I2C_BUS, 0x26, 0xa2 }, + { OV511_I2C_BUS, 0x27, 0xea }, + { OV511_I2C_BUS, 0x28, 0x00 }, + { OV511_I2C_BUS, 0x29, 0x00 }, + { OV511_I2C_BUS, 0x2a, 0x80 }, + { OV511_I2C_BUS, 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { OV511_I2C_BUS, 0x2c, 0xac }, + { OV511_I2C_BUS, 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { OV511_I2C_BUS, 0x2e, 0x80 }, + { OV511_I2C_BUS, 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { OV511_I2C_BUS, 0x4c, 0x00 }, + { OV511_I2C_BUS, 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { OV511_I2C_BUS, 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { OV511_I2C_BUS, 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { OV511_I2C_BUS, 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { OV511_I2C_BUS, 0x63, 0xff }, + { OV511_I2C_BUS, 0x64, 0x53 }, /* new windrv 090403 says 0x57, maybe thats wrong */ + { OV511_I2C_BUS, 0x65, 0x00 }, + { OV511_I2C_BUS, 0x66, 0x55 }, + { OV511_I2C_BUS, 0x67, 0xb0 }, + { OV511_I2C_BUS, 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { OV511_I2C_BUS, 0x69, 0x02 }, + { OV511_I2C_BUS, 0x6a, 0x22 }, + { OV511_I2C_BUS, 0x6b, 0x00 }, + { OV511_I2C_BUS, 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but deleting bit7 colors the first images red */ + { OV511_I2C_BUS, 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { OV511_I2C_BUS, 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { OV511_I2C_BUS, 0x6f, 0x01 }, + { OV511_I2C_BUS, 0x70, 0x8b }, + { OV511_I2C_BUS, 0x71, 0x00 }, + { OV511_I2C_BUS, 0x72, 0x14 }, + { OV511_I2C_BUS, 0x73, 0x54 }, + { OV511_I2C_BUS, 0x74, 0x00 },//0x60? /* was 0x00, new from windrv 090403 */ + { OV511_I2C_BUS, 0x75, 0x0e }, + { OV511_I2C_BUS, 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { OV511_I2C_BUS, 0x77, 0xff }, + { OV511_I2C_BUS, 0x78, 0x80 }, + { OV511_I2C_BUS, 0x79, 0x80 }, + { OV511_I2C_BUS, 0x7a, 0x80 }, + { OV511_I2C_BUS, 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { OV511_I2C_BUS, 0x7c, 0x00 }, + { OV511_I2C_BUS, 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { OV511_I2C_BUS, 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { OV511_I2C_BUS, 0x7f, 0xfb }, + { OV511_I2C_BUS, 0x80, 0x28 }, + { OV511_I2C_BUS, 0x81, 0x00 }, + { OV511_I2C_BUS, 0x82, 0x23 }, + { OV511_I2C_BUS, 0x83, 0x0b }, + { OV511_I2C_BUS, 0x84, 0x00 }, + { OV511_I2C_BUS, 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { OV511_I2C_BUS, 0x86, 0xc9 }, + { OV511_I2C_BUS, 0x87, 0x00 }, + { OV511_I2C_BUS, 0x88, 0x00 }, + { OV511_I2C_BUS, 0x89, 0x01 }, + { OV511_I2C_BUS, 0x12, 0x20 }, + { OV511_I2C_BUS, 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, "starting configuration"); + + if (init_ov_sensor(ov) < 0) { + err("Failed to read sensor ID. You might not have an"); + err("OV8600/10, or it might not be responding. Report"); + err("this to sensor = SEN_OV8610; + } else { + err("Unknown image sensor version: %d", rc & 3); + return -1; + } + + PDEBUG(4, "Writing 8610 registers"); + if (write_regvals(ov, regvals_norm_8610)) + return -1; + + /* Set sensor-specific vars */ + ov->maxwidth = 800; + ov->maxheight = 600; + ov->minwidth = 64; + ov->minheight = 48; + + // FIXME: These do not match the actual settings yet + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; + + return 0; +} + +/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses + * the same register settings as the OV7610, since they are very similar. + */ +static int +ov7xx0_configure(struct usb_ov511 *ov) +{ + int rc, high, low; + + /* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ + static struct ov511_regvals regvals_norm_7610[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x38, 0x81 }, + { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ + { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x20, 0x1c }, + { OV511_I2C_BUS, 0x23, 0x2a }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x26, 0xa2 }, + { OV511_I2C_BUS, 0x27, 0xc2 }, + { OV511_I2C_BUS, 0x2a, 0x04 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x2d, 0x93 }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals regvals_norm_7620[] = { + { OV511_I2C_BUS, 0x00, 0x00 }, + { OV511_I2C_BUS, 0x01, 0x80 }, + { OV511_I2C_BUS, 0x02, 0x80 }, + { OV511_I2C_BUS, 0x03, 0xc0 }, + { OV511_I2C_BUS, 0x06, 0x60 }, + { OV511_I2C_BUS, 0x07, 0x00 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x13, 0x01 }, + { OV511_I2C_BUS, 0x14, 0x84 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x16, 0x03 }, + { OV511_I2C_BUS, 0x17, 0x2f }, + { OV511_I2C_BUS, 0x18, 0xcf }, + { OV511_I2C_BUS, 0x19, 0x06 }, + { OV511_I2C_BUS, 0x1a, 0xf5 }, + { OV511_I2C_BUS, 0x1b, 0x00 }, + { OV511_I2C_BUS, 0x20, 0x18 }, + { OV511_I2C_BUS, 0x21, 0x80 }, + { OV511_I2C_BUS, 0x22, 0x80 }, + { OV511_I2C_BUS, 0x23, 0x00 }, + { OV511_I2C_BUS, 0x26, 0xa2 }, + { OV511_I2C_BUS, 0x27, 0xea }, + { OV511_I2C_BUS, 0x28, 0x20 }, + { OV511_I2C_BUS, 0x29, 0x00 }, + { OV511_I2C_BUS, 0x2a, 0x10 }, + { OV511_I2C_BUS, 0x2b, 0x00 }, + { OV511_I2C_BUS, 0x2c, 0x88 }, + { OV511_I2C_BUS, 0x2d, 0x91 }, + { OV511_I2C_BUS, 0x2e, 0x80 }, + { OV511_I2C_BUS, 0x2f, 0x44 }, + { OV511_I2C_BUS, 0x60, 0x27 }, + { OV511_I2C_BUS, 0x61, 0x02 }, + { OV511_I2C_BUS, 0x62, 0x5f }, + { OV511_I2C_BUS, 0x63, 0xd5 }, + { OV511_I2C_BUS, 0x64, 0x57 }, + { OV511_I2C_BUS, 0x65, 0x83 }, + { OV511_I2C_BUS, 0x66, 0x55 }, + { OV511_I2C_BUS, 0x67, 0x92 }, + { OV511_I2C_BUS, 0x68, 0xcf }, + { OV511_I2C_BUS, 0x69, 0x76 }, + { OV511_I2C_BUS, 0x6a, 0x22 }, + { OV511_I2C_BUS, 0x6b, 0x00 }, + { OV511_I2C_BUS, 0x6c, 0x02 }, + { OV511_I2C_BUS, 0x6d, 0x44 }, + { OV511_I2C_BUS, 0x6e, 0x80 }, + { OV511_I2C_BUS, 0x6f, 0x1d }, + { OV511_I2C_BUS, 0x70, 0x8b }, + { OV511_I2C_BUS, 0x71, 0x00 }, + { OV511_I2C_BUS, 0x72, 0x14 }, + { OV511_I2C_BUS, 0x73, 0x54 }, + { OV511_I2C_BUS, 0x74, 0x00 }, + { OV511_I2C_BUS, 0x75, 0x8e }, + { OV511_I2C_BUS, 0x76, 0x00 }, + { OV511_I2C_BUS, 0x77, 0xff }, + { OV511_I2C_BUS, 0x78, 0x80 }, + { OV511_I2C_BUS, 0x79, 0x80 }, + { OV511_I2C_BUS, 0x7a, 0x80 }, + { OV511_I2C_BUS, 0x7b, 0xe2 }, + { OV511_I2C_BUS, 0x7c, 0x00 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + /* 7640 and 7648. The defaults should be OK for most registers. */ + static struct ov511_regvals regvals_norm_7640[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, + { OV511_I2C_BUS, 0x12, 0x14 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, "starting configuration"); + + if (init_ov_sensor(ov) < 0) { + err("Failed to read sensor ID. You might not have an"); + err("OV76xx, or it might not be responding. Report"); + err("this to " EMAIL); + err("This is only a warning. You can attempt to use"); + err("your camera anyway"); + } else { + PDEBUG(1, "OV7xx0 initialized"); + } + + /* Detect sensor (sub)type */ + rc = i2c_r(ov, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if ((rc & 3) == 3) { + info("Sensor is an OV7610"); + ov->sensor = SEN_OV7610; + } else if ((rc & 3) == 1) { + /* I don't know what's different about the 76BE yet. */ + if (i2c_r(ov, 0x15) & 1) + info("Sensor is an OV7620AE"); + else + info("Sensor is an OV76BE"); + + /* OV511+ will return all zero isoc data unless we + * configure the sensor as a 7620. Someone needs to + * find the exact reg. setting that causes this. */ + if (ov->bridge == BRG_OV511PLUS) + ov->sensor = SEN_OV7620; + else + ov->sensor = SEN_OV76BE; + } else if ((rc & 3) == 0) { + /* try to read product id registers */ + high = i2c_r(ov, 0x0a); + if (high < 0) { + err("Error detecting camera chip PID"); + return high; + } + low = i2c_r(ov, 0x0b); + if (low < 0) { + err("Error detecting camera chip VER"); + return low; + } + if (high == 0x76) { + if (low == 0x30) { + info("Sensor is an OV7630/OV7635"); + ov->sensor = SEN_OV7630; + } + else if (low == 0x40) { + info("Sensor is an OV7645"); + ov->sensor = SEN_OV7640; // FIXME + } + else if (low == 0x45) { + info("Sensor is an OV7645B"); + ov->sensor = SEN_OV7640; // FIXME + } + else if (low == 0x48) { + info("Sensor is an OV7648"); + ov->sensor = SEN_OV7640; // FIXME + } + else { + err("Unknown sensor: 0x76%X", low); + return -1; + } + } else { + info("Sensor is an OV7620"); + ov->sensor = SEN_OV7620; + } + } else { + err("Unknown image sensor version: %d", rc & 3); + return -1; + } + + if (ov->sensor == SEN_OV7620) { + PDEBUG(4, "Writing 7620 registers"); + if (write_regvals(ov, regvals_norm_7620)) + return -1; + } else if (ov->sensor == SEN_OV7630) { + PDEBUG(4, "7630 is not supported by this driver version"); + return -1; + } else if (ov->sensor == SEN_OV7640) { + PDEBUG(4, "Writing 7640 registers"); + if (write_regvals(ov, regvals_norm_7640)) + return -1; + } else { + PDEBUG(4, "Writing 7610 registers"); + if (write_regvals(ov, regvals_norm_7610)) + return -1; + } + + /* Set sensor-specific vars */ + ov->maxwidth = 640; + ov->maxheight = 480; + ov->minwidth = 64; + ov->minheight = 48; + + // FIXME: These do not match the actual settings yet + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; + + return 0; +} + +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ +static int +ov6xx0_configure(struct usb_ov511 *ov) +{ + int rc; + + static struct ov511_regvals regvals_norm_6x20[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x03, 0x60 }, + { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ + { OV511_I2C_BUS, 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */ + { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */ + { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */ + { OV511_I2C_BUS, 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { OV511_I2C_BUS, 0x16, 0x06 }, +// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ + { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { OV511_I2C_BUS, 0x28, 0x05 }, + { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ +// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ + { OV511_I2C_BUS, 0x2d, 0x99 }, + { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Processing Parameter */ + { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */ + { OV511_I2C_BUS, 0x38, 0x8b }, + { OV511_I2C_BUS, 0x39, 0x40 }, + + { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ + { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ + { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { OV511_I2C_BUS, 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + { OV511_I2C_BUS, 0x4a, 0x80 }, + { OV511_I2C_BUS, 0x4b, 0x80 }, + { OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */ + { OV511_I2C_BUS, 0x4e, 0xc1 }, + { OV511_I2C_BUS, 0x4f, 0x04 }, +// Do 50-53 have any effect? +// Toggle 0x12[2] off and on here? + { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */ + }; + + static struct ov511_regvals regvals_norm_6x30[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, /* Reset */ + { OV511_I2C_BUS, 0x00, 0x1f }, /* Gain */ + { OV511_I2C_BUS, 0x01, 0x99 }, /* Blue gain */ + { OV511_I2C_BUS, 0x02, 0x7c }, /* Red gain */ + { OV511_I2C_BUS, 0x03, 0xc0 }, /* Saturation */ + { OV511_I2C_BUS, 0x05, 0x0a }, /* Contrast */ + { OV511_I2C_BUS, 0x06, 0x95 }, /* Brightness */ + { OV511_I2C_BUS, 0x07, 0x2d }, /* Sharpness */ + { OV511_I2C_BUS, 0x0c, 0x20 }, + { OV511_I2C_BUS, 0x0d, 0x20 }, + { OV511_I2C_BUS, 0x0e, 0x20 }, + { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x10, 0x9a }, + { OV511_I2C_BUS, 0x11, 0x00 }, /* Pixel clock = fastest */ + { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC and AWB */ + { OV511_I2C_BUS, 0x13, 0x21 }, + { OV511_I2C_BUS, 0x14, 0x80 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x16, 0x03 }, + { OV511_I2C_BUS, 0x17, 0x38 }, + { OV511_I2C_BUS, 0x18, 0xea }, + { OV511_I2C_BUS, 0x19, 0x04 }, + { OV511_I2C_BUS, 0x1a, 0x93 }, + { OV511_I2C_BUS, 0x1b, 0x00 }, + { OV511_I2C_BUS, 0x1e, 0xc4 }, + { OV511_I2C_BUS, 0x1f, 0x04 }, + { OV511_I2C_BUS, 0x20, 0x20 }, + { OV511_I2C_BUS, 0x21, 0x10 }, + { OV511_I2C_BUS, 0x22, 0x88 }, + { OV511_I2C_BUS, 0x23, 0xc0 }, /* Crystal circuit power level */ + { OV511_I2C_BUS, 0x25, 0x9a }, /* Increase AEC black ratio */ + { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ + { OV511_I2C_BUS, 0x27, 0xa2 }, + { OV511_I2C_BUS, 0x28, 0x00 }, + { OV511_I2C_BUS, 0x29, 0x00 }, + { OV511_I2C_BUS, 0x2a, 0x84 }, /* 60 Hz power */ + { OV511_I2C_BUS, 0x2b, 0xa8 }, /* 60 Hz power */ + { OV511_I2C_BUS, 0x2c, 0xa0 }, + { OV511_I2C_BUS, 0x2d, 0x95 }, /* Enable auto-brightness */ + { OV511_I2C_BUS, 0x2e, 0x88 }, + { OV511_I2C_BUS, 0x33, 0x26 }, + { OV511_I2C_BUS, 0x34, 0x03 }, + { OV511_I2C_BUS, 0x36, 0x8f }, + { OV511_I2C_BUS, 0x37, 0x80 }, + { OV511_I2C_BUS, 0x38, 0x83 }, + { OV511_I2C_BUS, 0x39, 0x80 }, + { OV511_I2C_BUS, 0x3a, 0x0f }, + { OV511_I2C_BUS, 0x3b, 0x3c }, + { OV511_I2C_BUS, 0x3c, 0x1a }, + { OV511_I2C_BUS, 0x3d, 0x80 }, + { OV511_I2C_BUS, 0x3e, 0x80 }, + { OV511_I2C_BUS, 0x3f, 0x0e }, + { OV511_I2C_BUS, 0x40, 0x00 }, /* White bal */ + { OV511_I2C_BUS, 0x41, 0x00 }, /* White bal */ + { OV511_I2C_BUS, 0x42, 0x80 }, + { OV511_I2C_BUS, 0x43, 0x3f }, /* White bal */ + { OV511_I2C_BUS, 0x44, 0x80 }, + { OV511_I2C_BUS, 0x45, 0x20 }, + { OV511_I2C_BUS, 0x46, 0x20 }, + { OV511_I2C_BUS, 0x47, 0x80 }, + { OV511_I2C_BUS, 0x48, 0x7f }, + { OV511_I2C_BUS, 0x49, 0x00 }, + { OV511_I2C_BUS, 0x4a, 0x00 }, + { OV511_I2C_BUS, 0x4b, 0x80 }, + { OV511_I2C_BUS, 0x4c, 0xd0 }, + { OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { OV511_I2C_BUS, 0x4e, 0x40 }, + { OV511_I2C_BUS, 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { OV511_I2C_BUS, 0x50, 0xff }, + { OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { OV511_I2C_BUS, 0x55, 0xff }, + { OV511_I2C_BUS, 0x56, 0x12 }, + { OV511_I2C_BUS, 0x57, 0x81 }, + { OV511_I2C_BUS, 0x58, 0x75 }, + { OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { OV511_I2C_BUS, 0x5a, 0x2c }, + { OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */ + { OV511_I2C_BUS, 0x5c, 0x10 }, + { OV511_I2C_BUS, 0x3d, 0x80 }, + { OV511_I2C_BUS, 0x27, 0xa6 }, + { OV511_I2C_BUS, 0x12, 0x20 }, /* Toggle AWB */ + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */ + }; + + PDEBUG(4, "starting sensor configuration"); + + if (init_ov_sensor(ov) < 0) { + err("Failed to read sensor ID. You might not have an OV6xx0,"); + err("or it may be not responding. Report this to " EMAIL); + return -1; + } else { + PDEBUG(1, "OV6xx0 sensor detected"); + } + + /* Detect sensor (sub)type */ + rc = i2c_r(ov, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } + + /* Ugh. The first two bits are the version bits, but the entire register + * value must be used. I guess OVT underestimated how many variants + * they would make. */ + if (rc == 0x00) { + ov->sensor = SEN_OV6630; + info("WARNING: Sensor is an OV66308. Your camera may have"); + info("been misdetected in previous driver versions. Please"); + info("report this to Mark."); + } else if (rc == 0x01) { + ov->sensor = SEN_OV6620; + info("Sensor is an OV6620"); + } else if (rc == 0x02) { + ov->sensor = SEN_OV6630; + info("Sensor is an OV66308AE"); + } else if (rc == 0x03) { + ov->sensor = SEN_OV6630; + info("Sensor is an OV66308AF"); + } else if (rc == 0x90) { + ov->sensor = SEN_OV6630; + info("WARNING: Sensor is an OV66307. Your camera may have"); + info("been misdetected in previous driver versions. Please"); + info("report this to Mark."); + } else { + err("FATAL: Unknown sensor version: 0x%02x", rc); + return -1; + } + + /* Set sensor-specific vars */ + ov->maxwidth = 352; + ov->maxheight = 288; + ov->minwidth = 64; + ov->minheight = 48; + + // FIXME: These do not match the actual settings yet + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; + + if (ov->sensor == SEN_OV6620) { + PDEBUG(4, "Writing 6x20 registers"); + if (write_regvals(ov, regvals_norm_6x20)) + return -1; + } else { + PDEBUG(4, "Writing 6x30 registers"); + if (write_regvals(ov, regvals_norm_6x30)) + return -1; + } + + return 0; +} + +/* This initializes the KS0127 and KS0127B video decoders. */ +static int +ks0127_configure(struct usb_ov511 *ov) +{ + int rc; + + /* Detect decoder subtype */ + rc = i2c_r(ov, 0x00); + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if (rc & 0x08) { + rc = i2c_r(ov, 0x3d); + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if ((rc & 0x0f) == 0) { + info("Sensor is a KS0127"); + } else if ((rc & 0x0f) == 9) { + info("Sensor is a KS0127B Rev. A"); + } + } else { + info("Sensor is a KS0122"); + } + + /* This device is not supported yet. Bail out now... */ + err("This sensor is not supported yet."); + return -1; +} + +/* This initializes the SAA7111A video decoder. */ +static int +saa7111a_configure(struct usb_ov511 *ov) +{ + int rc; + + /* Since there is no register reset command, all registers must be + * written, otherwise gives erratic results */ + static struct ov511_regvals regvals_norm_SAA7111A[] = { + { OV511_I2C_BUS, 0x06, 0xce }, + { OV511_I2C_BUS, 0x07, 0x00 }, + { OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */ + { OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */ + { OV511_I2C_BUS, 0x00, 0x00 }, + { OV511_I2C_BUS, 0x01, 0x00 }, + { OV511_I2C_BUS, 0x03, 0x23 }, + { OV511_I2C_BUS, 0x04, 0x00 }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */ + { OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */ + { OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */ + { OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */ + { OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */ + { OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */ + { OV511_I2C_BUS, 0x0f, 0x00 }, + { OV511_I2C_BUS, 0x11, 0x0c }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x13, 0x00 }, + { OV511_I2C_BUS, 0x14, 0x00 }, + { OV511_I2C_BUS, 0x15, 0x00 }, + { OV511_I2C_BUS, 0x16, 0x00 }, + { OV511_I2C_BUS, 0x17, 0x00 }, + { OV511_I2C_BUS, 0x02, 0xc0 }, /* Composite input 0 */ + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + /* 640x480 not supported with PAL */ + if (ov->pal) { + ov->maxwidth = 320; + ov->maxheight = 240; /* Even field only */ + } else { + ov->maxwidth = 640; + ov->maxheight = 480; /* Even/Odd fields */ + } + + ov->minwidth = 320; + ov->minheight = 240; /* Even field only */ + + ov->has_decoder = 1; + ov->num_inputs = 8; + ov->norm = VIDEO_MODE_AUTO; + ov->stop_during_set = 0; /* Decoder guarantees stable image */ + + /* Decoder doesn't change these values, so we use these instead of + * acutally reading the registers (which doesn't work) */ + ov->brightness = 0x80 << 8; + ov->contrast = 0x40 << 9; + ov->colour = 0x40 << 9; + ov->hue = 32768; + + PDEBUG(4, "Writing SAA7111A registers"); + if (write_regvals(ov, regvals_norm_SAA7111A)) + return -1; + + /* Detect version of decoder. This must be done after writing the + * initial regs or the decoder will lock up. */ + rc = i2c_r(ov, 0x00); + + if (rc < 0) { + err("Error detecting sensor version"); + return -1; + } else { + info("Sensor is an SAA7111A (version 0x%x)", rc); + ov->sensor = SEN_SAA7111A; + } + + /* Latch to negative edge of clock. Otherwise, we get incorrect + * colors and jitter in the digital signal. */ + if (ov->bclass == BCL_OV511) + reg_w(ov, 0x11, 0x00); + else + warn("SAA7111A not yet supported with OV518/OV518+"); + + return 0; +} + +/* This initializes the OV511/OV511+ and the sensor */ +static int +ov511_configure(struct usb_ov511 *ov) +{ + static struct ov511_regvals regvals_init_511[] = { + { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3d }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; + + static struct ov511_regvals regvals_norm_511[] = { + { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0x01 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R511_FIFO_OPTS, 0x1f }, + { OV511_REG_BUS, R511_COMP_EN, 0x00 }, + { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals regvals_norm_511_plus[] = { + { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0xff }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R511_FIFO_OPTS, 0xff }, + { OV511_REG_BUS, R511_COMP_EN, 0x00 }, + { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, ""); + + ov->customid = reg_r(ov, R511_SYS_CUST_ID); + if (ov->customid < 0) { + err("Unable to read camera bridge registers"); + goto error; + } + + PDEBUG (1, "CustomID = %d", ov->customid); + ov->desc = symbolic(camlist, ov->customid); + info("model: %s", ov->desc); + + if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) { + err("Camera type (%d) not recognized", ov->customid); + err("Please notify " EMAIL " of the name,"); + err("manufacturer, model, and this number of your camera."); + err("Also include the output of the detection process."); + } + + if (ov->customid == 70) /* USB Life TV (PAL/SECAM) */ + ov->pal = 1; + + if (write_regvals(ov, regvals_init_511)) goto error; + + if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); + + /* The OV511+ has undocumented bits in the flow control register. + * Setting it to 0xff fixes the corruption with moving objects. */ + if (ov->bridge == BRG_OV511) { + if (write_regvals(ov, regvals_norm_511)) goto error; + } else if (ov->bridge == BRG_OV511PLUS) { + if (write_regvals(ov, regvals_norm_511_plus)) goto error; + } else { + err("Invalid bridge"); + } + + if (ov511_init_compression(ov)) goto error; + + ov->packet_numbering = 1; + ov511_set_packet_size(ov, 0); + + ov->snap_enabled = snapshot; + + /* Test for 7xx0 */ + PDEBUG(3, "Testing for 0V7xx0"); + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) + goto error; + + if (i2c_w(ov, 0x12, 0x80) < 0) { + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) + goto error; + + if (i2c_w(ov, 0x12, 0x80) < 0) { + /* Test for 8xx0 */ + PDEBUG(3, "Testing for 0V8xx0"); + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) + goto error; + + if (i2c_w(ov, 0x12, 0x80) < 0) { + /* Test for SAA7111A */ + PDEBUG(3, "Testing for SAA7111A"); + ov->primary_i2c_slave = SAA7111A_SID; + if (ov51x_set_slave_ids(ov, SAA7111A_SID) < 0) + goto error; + + if (i2c_w(ov, 0x0d, 0x00) < 0) { + /* Test for KS0127 */ + PDEBUG(3, "Testing for KS0127"); + ov->primary_i2c_slave = KS0127_SID; + if (ov51x_set_slave_ids(ov, KS0127_SID) < 0) + goto error; + + if (i2c_w(ov, 0x10, 0x00) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + if (ks0127_configure(ov) < 0) { + err("Failed to configure KS0127"); + goto error; + } + } + } else { + if (saa7111a_configure(ov) < 0) { + err("Failed to configure SAA7111A"); + goto error; + } + } + } else { + if (ov8xx0_configure(ov) < 0) { + err("Failed to configure OV8xx0 sensor"); + goto error; + } + } + } else { + if (ov6xx0_configure(ov) < 0) { + err("Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(ov) < 0) { + err("Failed to configure OV7xx0"); + goto error; + } + } + + return 0; + +error: + err("OV511 Config failed"); + + return -EBUSY; +} + +/* This initializes the OV518/OV518+ and the sensor */ +static int +ov518_configure(struct usb_ov511 *ov) +{ + /* For 518 and 518+ */ + static struct ov511_regvals regvals_init_518[] = { + { OV511_REG_BUS, R51x_SYS_RESET, 0x40 }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3e }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x00 }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, 0x46, 0x00 }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; + + static struct ov511_regvals regvals_norm_518[] = { + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ + { OV511_REG_BUS, 0x31, 0x0f }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, 0x20, 0x00 }, + { OV511_REG_BUS, 0x51, 0x04 }, + { OV511_REG_BUS, 0x71, 0x19 }, + { OV511_REG_BUS, 0x2f, 0x80 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals regvals_norm_518_plus[] = { + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ + { OV511_REG_BUS, 0x31, 0x0f }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, 0x20, 0x60 }, + { OV511_REG_BUS, 0x51, 0x02 }, + { OV511_REG_BUS, 0x71, 0x19 }, + { OV511_REG_BUS, 0x40, 0xff }, + { OV511_REG_BUS, 0x41, 0x42 }, + { OV511_REG_BUS, 0x46, 0x00 }, + { OV511_REG_BUS, 0x33, 0x04 }, + { OV511_REG_BUS, 0x21, 0x19 }, + { OV511_REG_BUS, 0x3f, 0x10 }, + { OV511_REG_BUS, 0x2f, 0x80 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, ""); + + /* First 5 bits of custom ID reg are a revision ID on OV518 */ + info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID)); + + /* Give it the default description */ + ov->desc = symbolic(camlist, 0); + + if (write_regvals(ov, regvals_init_518)) goto error; + + /* Set LED GPIO pin to output mode */ + if (reg_w_mask(ov, 0x57, 0x00, 0x02) < 0) goto error; + + /* LED is off by default with OV518; have to explicitly turn it on */ + if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); + else + ov51x_led_control(ov, 1); + + /* Don't require compression if dumppix is enabled; otherwise it's + * required. OV518 has no uncompressed mode, to save RAM. */ + if (!dumppix && !ov->compress) { + ov->compress = 1; + warn("Compression required with OV518...enabling"); + } + + if (ov->bridge == BRG_OV518) { + if (write_regvals(ov, regvals_norm_518)) goto error; + } else if (ov->bridge == BRG_OV518PLUS) { + if (write_regvals(ov, regvals_norm_518_plus)) goto error; + } else { + err("Invalid bridge"); + } + + if (ov518_init_compression(ov)) goto error; + + if (ov->bridge == BRG_OV518) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7) + struct usb_interface *ifp; + struct usb_host_interface *alt; + __u16 mxps = 0; + + ifp = usb_ifnum_to_if(ov->dev, 0); + if (ifp) { + alt = usb_altnum_to_altsetting(ifp, 7); + if (alt) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + mxps = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); +# else + mxps = alt->endpoint[0].desc.wMaxPacketSize; +# endif + } +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + struct usb_interface *ifp = ov->dev->config[0].interface[0]; + __u16 mxps = ifp->altsetting[7].endpoint[0].desc.wMaxPacketSize; +#else + struct usb_interface *ifp = &ov->dev->config[0].interface[0]; + __u16 mxps = ifp->altsetting[7].endpoint[0].wMaxPacketSize; +#endif + /* Some OV518s have packet numbering by default, some don't */ + if (mxps == 897) + ov->packet_numbering = 1; + else + ov->packet_numbering = 0; + } else { + /* OV518+ has packet numbering turned on by default */ + ov->packet_numbering = 1; + } + + ov518_set_packet_size(ov, 0); + + ov->snap_enabled = snapshot; + + /* Test for 76xx */ + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) + goto error; + + /* The OV518 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + + if (init_ov_sensor(ov) < 0) { + /* Test for 6xx0 */ + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) + goto error; + + if (init_ov_sensor(ov) < 0) { + /* Test for 8xx0 */ + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) + goto error; + + if (init_ov_sensor(ov) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + if (ov8xx0_configure(ov) < 0) { + err("Failed to configure OV8xx0 sensor"); + goto error; + } + } + } else { + if (ov6xx0_configure(ov) < 0) { + err("Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(ov) < 0) { + err("Failed to configure OV7xx0"); + goto error; + } + } + + ov->maxwidth = 352; + ov->maxheight = 288; + + // The OV518 cannot go as low as the sensor can + ov->minwidth = 160; + ov->minheight = 120; + + return 0; + +error: + err("OV518 Config failed"); + + return -EBUSY; +} + +/* This initializes the OV518/OV518+ and the sensor */ +static int +ov519_configure(struct usb_ov511 *ov) +{ + + static struct ov511_regvals regvals_init_519[] = { + { OV511_REG_BUS, 0x5a, 0x6d }, /* EnableSystem */ + /* windows reads 0x53 at this point*/ + { OV511_REG_BUS, 0x53, 0x9b }, + { OV511_REG_BUS, 0x54, 0x0f }, // set bit2 to enable jpeg + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x49, 0x01 }, + { OV511_REG_BUS, 0x48, 0x00 }, + + /* Set LED pin to output mode. Bit 4 must be cleared or sensor + * detection will fail. This deserves further investigation. */ + { OV511_REG_BUS, OV519_GPIO_IO_CTRL0, 0xee }, + + { OV511_REG_BUS, 0x51, 0x0f }, /* SetUsbInit */ + { OV511_REG_BUS, 0x51, 0x00 }, + { OV511_REG_BUS, 0x22, 0x00 }, + /* windows reads 0x55 at this point*/ + { OV511_DONE_BUS, 0x0, 0x00}, + }; + + PDEBUG(4, ""); + + /* Give it the default description */ + ov->desc = symbolic(camlist, 0); + + if (write_regvals(ov, regvals_init_519)) + goto error; + + if (ov519_init_compression(ov)) + goto error; + + if (ov->imp == IMP_EYETOY) { + /* LED is annoyingly bright. Only turn it on if requested to. */ + if (ov->led2_policy == LED_ON) + ov51x_led_control(ov, 1); + else + ov51x_led_control(ov, 0); + } else { + /* LED might be off by default */ + if (ov->led_policy == LED_ON) + ov51x_led_control(ov, 1); + else + ov51x_led_control(ov, 0); + } + + /* Don't require compression if dumppix is enabled; otherwise it's + * required. OV519 probably has no uncompressed mode, to save RAM. */ + if (!dumppix && !ov->compress) + ov->compress = 1; + + ov->packet_numbering = 0; + + ov519_set_packet_size(ov, 0); + + ov->snap_enabled = snapshot; + + /* Test for 76xx */ + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) + goto error; + + /* The OV519 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + + if (init_ov_sensor(ov) < 0) { + /* Test for 6xx0 */ + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) + goto error; + + if (init_ov_sensor(ov) < 0) { + /* Test for 8xx0 */ + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) + goto error; + + if (init_ov_sensor(ov) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + if (ov8xx0_configure(ov) < 0) { + err("Failed to configure OV8xx0 sensor"); + goto error; + } + } + } else { + if (ov6xx0_configure(ov) < 0) { + err("Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(ov) < 0) { + err("Failed to configure OV7xx0"); + goto error; + } + } + + return 0; + +error: + err("OV519 Config failed"); + + return -EBUSY; +} + +/**************************************************************************** + * + * USB routines + * + ***************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36) +static int +ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) +static void * +ov51x_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ +#else +static void * +ov51x_probe(struct usb_device *dev, unsigned int ifnum) +{ +#endif + struct usb_interface_descriptor *idesc; + struct usb_ov511 *ov; + int i; + u16 vendor, product; + + PDEBUG(1, "probing for device..."); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36) + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; +#else + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7) + idesc = &intf->cur_altsetting->desc; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36) + idesc = &intf->altsetting[0].desc; +#else + idesc = &dev->actconfig->interface[ifnum].altsetting[0]; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + vendor = le16_to_cpu(dev->descriptor.idVendor); + product = le16_to_cpu(dev->descriptor.idProduct); +#else + vendor = dev->descriptor.idVendor; + product = dev->descriptor.idProduct; +#endif + +/* 2.2.x compatibility */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0) + /* Is it an OV511/OV511+? */ + if (vendor != VEND_OMNIVISION + && vendor != VEND_MATTEL + && vendor != VEND_SONY) + return NULL; + + if (vendor == VEND_OMNIVISION + && product != PROD_OV511 + && product != PROD_OV518 + && product != PROD_OV511PLUS + && product != PROD_OV518PLUS + && product != PROD_OV519 + && product != PROD_OV1519 + && product != PROD_OV2519 + && product != PROD_OV3519 + && product != PROD_OV4519 + && product != PROD_OV5519 + && product != PROD_OV6519 + && product != PROD_OV7519 + && product != PROD_OV8519 + && product != PROD_OV9519 + && product != PROD_OVA519 + && product != PROD_OVB519 + && product != PROD_OVC519 + && product != PROD_OVD519 + && product != PROD_OVE519 + && product != PROD_OVF519 + && product != PROD_OV530) + return NULL; + + if (vendor == VEND_MATTEL + && product != PROD_ME2CAM) + return NULL; + + if (vendor == VEND_SONY + && product != PROD_EYETOY4 + && product != PROD_EYETOY5) + return NULL; + + if (vendor == VEND_MICROSOFT + && product != PROD_XBOX_CAM) + return NULL; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36) + if (idesc->bInterfaceClass != 0xFF) + return -ENODEV; + if (idesc->bInterfaceSubClass != 0x00) + return -ENODEV; +#else + if (idesc->bInterfaceClass != 0xFF) + return NULL; + if (idesc->bInterfaceSubClass != 0x00) + return NULL; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) + /* Since code below may sleep, we use this as a lock */ + MOD_INC_USE_COUNT; +#endif + + if ((ov = kmalloc(sizeof(*ov), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc ov struct"); + goto error_out; + } + + memset(ov, 0, sizeof(*ov)); + + ov->dev = dev; + ov->iface = idesc->bInterfaceNumber; + ov->led_policy = led; + ov->led2_policy = led2; + ov->compress = compress; + ov->lightfreq = lightfreq; + ov->num_inputs = 1; /* Video decoder init functs. change this */ + ov->stop_during_set = !fastset; + ov->backlight = backlight; + ov->mirror = mirror; + ov->auto_brt = autobright; + ov->auto_gain = autogain; + ov->auto_exp = autoexp; + ov->imp = IMP_GENERIC; + + switch (product) { + case PROD_OV511: + ov->bridge = BRG_OV511; + ov->bclass = BCL_OV511; + break; + case PROD_OV511PLUS: + ov->bridge = BRG_OV511PLUS; + ov->bclass = BCL_OV511; + break; + case PROD_OV518: + ov->bridge = BRG_OV518; + ov->bclass = BCL_OV518; + break; + case PROD_OV518PLUS: + ov->bridge = BRG_OV518PLUS; + ov->bclass = BCL_OV518; + break; + case PROD_OV519: + case PROD_OV4519: + case PROD_OV8519: + case PROD_OV530: + case PROD_XBOX_CAM: + ov->bridge = BRG_OV519; + ov->bclass = BCL_OV519; + break; + case PROD_OV1519: + case PROD_OV2519: + case PROD_OV3519: + case PROD_OV5519: + case PROD_OV6519: + case PROD_OV7519: + case PROD_OV9519: + case PROD_OVA519: + case PROD_OVB519: + case PROD_OVC519: + case PROD_OVD519: + case PROD_OVE519: + case PROD_OVF519: + info("Device has Product ID that hasn't been seen yet. It"); + info("will probably work anyway, but please send"); + info("/proc/bus/usb/devices, your dmesg log, and any info you"); + info("have about this device to mark@alpha.dyndns.org"); + ov->bridge = BRG_OV519; + ov->bclass = BCL_OV519; + break; + case PROD_EYETOY4: + case PROD_EYETOY5: + /* These two should work, but they are untested */ +// case PROD_EYETOY6: +// case PROD_EYETOY7: + ov->bridge = BRG_OV519; + ov->bclass = BCL_OV519; + ov->imp = IMP_EYETOY; + break; + case PROD_ME2CAM: + if (vendor != VEND_MATTEL) + goto error; + ov->bridge = BRG_OV511PLUS; + ov->bclass = BCL_OV511; + break; + default: + err("Unknown product ID 0x%04x", product); + goto error; + } + + info("USB %s video device found", symbolic(brglist, ov->bridge)); + +#ifdef OV511_ALLOW_CONVERSION + /* Workaround for some applications that want data in RGB + * instead of BGR. */ + if (force_rgb) + info("data format set to RGB"); +#endif + + init_waitqueue_head(&ov->wq); + + init_MUTEX(&ov->lock); /* to 1 == available */ + init_MUTEX(&ov->buf_lock); + init_MUTEX(&ov->param_lock); + init_MUTEX(&ov->i2c_lock); + init_MUTEX(&ov->cbuf_lock); + + ov->buf_state = BUF_NOT_ALLOCATED; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20) + if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) { + err("usb_make_path error"); + goto error; + } +#endif + + /* Allocate control transfer buffer. */ + /* Must be kmalloc()'ed, for DMA compatibility */ + ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL); + if (!ov->cbuf) + goto error; + + switch (ov->bclass) { + case BCL_OV511: + if (ov511_configure(ov) < 0) + goto error; + break; + case BCL_OV518: + if (ov518_configure(ov) < 0) + goto error; + break; + case BCL_OV519: + if (ov519_configure(ov) < 0) + goto error; + break; + default: + goto error; + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].framenum = i; + init_waitqueue_head(&ov->frame[i].wq); + } + + for (i = 0; i < OV511_NUMSBUF; i++) { + ov->sbuf[i].ov = ov; + spin_lock_init(&ov->sbuf[i].lock); + ov->sbuf[i].n = i; + } + + /* Unnecessary? (This is done on open(). Need to make sure variables + * are properly initialized without this before removing it, though). */ + if (ov51x_set_default_params(ov) < 0) + goto error; + +#ifdef OV511_DEBUG + if (dump_bridge) { + if (ov->bclass == BCL_OV511) + ov511_dump_regs(ov); + else + ov518_dump_regs(ov); + } +#endif + + ov->vdev = video_device_alloc(); + if (!ov->vdev) + goto error; + + memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + ov->vdev->dev = &dev->dev; +#endif + video_set_drvdata(ov->vdev, ov); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 5) + for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { + /* Minor 0 cannot be specified; assume user wants autodetect */ + if (unit_video[i] == 0) + break; + + if (video_register_device(ov->vdev, VFL_TYPE_GRABBER, + unit_video[i]) >= 0) { + break; + } + } + + /* Use the next available one */ + if ((ov->vdev->minor == -1) && + video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1) < 0) { +#else + if (video_register_device(ov->vdev, VFL_TYPE_GRABBER) < 0) { +#endif + err("video_register_device failed"); + goto error; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20) + info("Device at %s registered to minor %d", ov->usb_path, + ov->vdev->minor); +#else + info("Device %d on bus %d registered to minor %d", dev->devnum, + dev->bus->busnum, ov->vdev->minor); +#endif + + create_proc_ov511_cam(ov); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) + MOD_DEC_USE_COUNT; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36) + usb_set_intfdata(intf, ov); + return 0; +#else + return ov; +#endif + +error: + destroy_proc_ov511_cam(ov); + + if (ov->vdev) { + if (-1 == ov->vdev->minor) + video_device_release(ov->vdev); + else + video_unregister_device(ov->vdev); + ov->vdev = NULL; + } + + if (ov->cbuf) { + down(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + up(&ov->cbuf_lock); + } + + kfree(ov); + ov = NULL; + +error_out: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) + MOD_DEC_USE_COUNT; +#endif + err("Camera initialization failed"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36) + return -EIO; +#else + return NULL; +#endif +} + +static void +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +ov51x_disconnect(struct usb_interface *intf) +{ + struct usb_ov511 *ov = usb_get_intfdata(intf); +#else +ov51x_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_ov511 *ov = (struct usb_ov511 *) ptr; +#endif + int n; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) + MOD_INC_USE_COUNT; +#endif + + PDEBUG(3, ""); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + usb_set_intfdata(intf, NULL); +#endif + if (!ov) + return; + +#ifdef OV511_OLD_V4L + /* We don't want people trying to open up the device */ + if (!ov->user) + video_unregister_device(ov->vdev); + else + PDEBUG(3, "Device open...deferring video_unregister_device"); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + if (ov->vdev) + video_unregister_device(ov->vdev); +#else + video_unregister_device(ov->vdev); + if (ov->user) + PDEBUG(3, "Device open...deferring video_unregister_device"); +#endif + + for (n = 0; n < OV511_NUMFRAMES; n++) + ov->frame[n].grabstate = FRAME_ERROR; + + ov->curframe = -1; + + /* This will cause the process to request another frame */ + for (n = 0; n < OV511_NUMFRAMES; n++) + wake_up_interruptible(&ov->frame[n].wq); + + wake_up_interruptible(&ov->wq); + + ov->streaming = 0; + ov51x_unlink_isoc(ov); + + destroy_proc_ov511_cam(ov); + + ov->dev = NULL; + + /* Free the memory */ + if (ov && !ov->user) { + down(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + up(&ov->cbuf_lock); + + ov51x_dealloc(ov); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) + if (ov->vdev) + video_device_release(ov->vdev); +#endif + kfree(ov); + ov = NULL; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20) + MOD_DEC_USE_COUNT; +#endif + PDEBUG(3, "Disconnect complete"); +} + +static struct usb_driver ov511_driver = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20) + .owner = THIS_MODULE, +#endif + .name = "ov51x", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) + .id_table = device_table, +#endif + .probe = ov51x_probe, + .disconnect = ov51x_disconnect +}; + + + +module_init(usb_ov511_init); +module_exit(usb_ov511_exit); +#endif +#endif diff --git a/plugins/USBqemu/usb-mic/adcuser.h b/plugins/USBqemu/usb-mic/adcuser.h new file mode 100644 index 0000000000..6dfb92d440 --- /dev/null +++ b/plugins/USBqemu/usb-mic/adcuser.h @@ -0,0 +1,30 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: ADCUSER.H + * Purpose: Audio Device Class Custom User Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __ADCUSER_H__ +#define __ADCUSER_H__ + + +/* Audio Device Class Requests Callback Functions */ +extern BOOL ADC_IF_GetRequest (void); +extern BOOL ADC_IF_SetRequest (void); +extern BOOL ADC_EP_GetRequest (void); +extern BOOL ADC_EP_SetRequest (void); + + +#endif /* __ADCUSER_H__ */ diff --git a/plugins/USBqemu/usb-mic/audio.h b/plugins/USBqemu/usb-mic/audio.h new file mode 100644 index 0000000000..69083fe9b8 --- /dev/null +++ b/plugins/USBqemu/usb-mic/audio.h @@ -0,0 +1,372 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: AUDIO.H + * Purpose: USB Audio Device Class Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __AUDIO_H__ +#define __AUDIO_H__ + + +/* Audio Interface Subclass Codes */ +#define AUDIO_SUBCLASS_UNDEFINED 0x00 +#define AUDIO_SUBCLASS_AUDIOCONTROL 0x01 +#define AUDIO_SUBCLASS_AUDIOSTREAMING 0x02 +#define AUDIO_SUBCLASS_MIDISTREAMING 0x03 + +/* Audio Interface Protocol Codes */ +#define AUDIO_PROTOCOL_UNDEFINED 0x00 + + +/* Audio Descriptor Types */ +#define AUDIO_UNDEFINED_DESCRIPTOR_TYPE 0x20 +#define AUDIO_DEVICE_DESCRIPTOR_TYPE 0x21 +#define AUDIO_CONFIGURATION_DESCRIPTOR_TYPE 0x22 +#define AUDIO_STRING_DESCRIPTOR_TYPE 0x23 +#define AUDIO_INTERFACE_DESCRIPTOR_TYPE 0x24 +#define AUDIO_ENDPOINT_DESCRIPTOR_TYPE 0x25 + + +/* Audio Control Interface Descriptor Subtypes */ +#define AUDIO_CONTROL_UNDEFINED 0x00 +#define AUDIO_CONTROL_HEADER 0x01 +#define AUDIO_CONTROL_INPUT_TERMINAL 0x02 +#define AUDIO_CONTROL_OUTPUT_TERMINAL 0x03 +#define AUDIO_CONTROL_MIXER_UNIT 0x04 +#define AUDIO_CONTROL_SELECTOR_UNIT 0x05 +#define AUDIO_CONTROL_FEATURE_UNIT 0x06 +#define AUDIO_CONTROL_PROCESSING_UNIT 0x07 +#define AUDIO_CONTROL_EXTENSION_UNIT 0x08 + +/* Audio Streaming Interface Descriptor Subtypes */ +#define AUDIO_STREAMING_UNDEFINED 0x00 +#define AUDIO_STREAMING_GENERAL 0x01 +#define AUDIO_STREAMING_FORMAT_TYPE 0x02 +#define AUDIO_STREAMING_FORMAT_SPECIFIC 0x03 + +/* Audio Endpoint Descriptor Subtypes */ +#define AUDIO_ENDPOINT_UNDEFINED 0x00 +#define AUDIO_ENDPOINT_GENERAL 0x01 + + +/* Audio Descriptor Sizes */ +#define AUDIO_CONTROL_INTERFACE_DESC_SZ(n) 0x08+n +#define AUDIO_STREAMING_INTERFACE_DESC_SIZE 0x07 +#define AUDIO_INPUT_TERMINAL_DESC_SIZE 0x0C +#define AUDIO_OUTPUT_TERMINAL_DESC_SIZE 0x09 +#define AUDIO_MIXER_UNIT_DESC_SZ(p,n) 0x0A+p+n +#define AUDIO_SELECTOR_UNIT_DESC_SZ(p) 0x06+p +#define AUDIO_FEATURE_UNIT_DESC_SZ(ch,n) 0x07+(ch+1)*n +#define AUDIO_PROCESSING_UNIT_DESC_SZ(p,n,x) 0x0D+p+n+x +#define AUDIO_EXTENSION_UNIT_DESC_SZ(p,n) 0x0D+p+n +#define AUDIO_STANDARD_ENDPOINT_DESC_SIZE 0x09 +#define AUDIO_STREAMING_ENDPOINT_DESC_SIZE 0x07 + + +/* Audio Processing Unit Process Types */ +#define AUDIO_UNDEFINED_PROCESS 0x00 +#define AUDIO_UP_DOWN_MIX_PROCESS 0x01 +#define AUDIO_DOLBY_PROLOGIC_PROCESS 0x02 +#define AUDIO_3D_STEREO_PROCESS 0x03 +#define AUDIO_REVERBERATION_PROCESS 0x04 +#define AUDIO_CHORUS_PROCESS 0x05 +#define AUDIO_DYN_RANGE_COMP_PROCESS 0x06 + + +/* Audio Request Codes */ +#define AUDIO_REQUEST_UNDEFINED 0x00 +#define AUDIO_REQUEST_SET_CUR 0x01 +#define AUDIO_REQUEST_GET_CUR 0x81 +#define AUDIO_REQUEST_SET_MIN 0x02 +#define AUDIO_REQUEST_GET_MIN 0x82 +#define AUDIO_REQUEST_SET_MAX 0x03 +#define AUDIO_REQUEST_GET_MAX 0x83 +#define AUDIO_REQUEST_SET_RES 0x04 +#define AUDIO_REQUEST_GET_RES 0x84 +#define AUDIO_REQUEST_SET_MEM 0x05 +#define AUDIO_REQUEST_GET_MEM 0x85 +#define AUDIO_REQUEST_GET_STAT 0xFF + + +/* Audio Control Selector Codes */ +#define AUDIO_CONTROL_UNDEFINED 0x00 /* Common Selector */ + +/* Terminal Control Selectors */ +#define AUDIO_COPY_PROTECT_CONTROL 0x01 + +/* Feature Unit Control Selectors */ +#define AUDIO_MUTE_CONTROL 0x01 +#define AUDIO_VOLUME_CONTROL 0x02 +#define AUDIO_BASS_CONTROL 0x03 +#define AUDIO_MID_CONTROL 0x04 +#define AUDIO_TREBLE_CONTROL 0x05 +#define AUDIO_GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AUDIO_AUTOMATIC_GAIN_CONTROL 0x07 +#define AUDIO_DELAY_CONTROL 0x08 +#define AUDIO_BASS_BOOST_CONTROL 0x09 +#define AUDIO_LOUDNESS_CONTROL 0x0A + +/* Processing Unit Control Selectors: */ +#define AUDIO_ENABLE_CONTROL 0x01 /* Common Selector */ +#define AUDIO_MODE_SELECT_CONTROL 0x02 /* Common Selector */ + +/* - Up/Down-mix Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ +/* AUDIO_MODE_SELECT_CONTROL 0x02 Common Selector */ + +/* - Dolby Prologic Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ +/* AUDIO_MODE_SELECT_CONTROL 0x02 Common Selector */ + +/* - 3D Stereo Extender Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ +#define AUDIO_SPACIOUSNESS_CONTROL 0x02 + +/* - Reverberation Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ +#define AUDIO_REVERB_LEVEL_CONTROL 0x02 +#define AUDIO_REVERB_TIME_CONTROL 0x03 +#define AUDIO_REVERB_FEEDBACK_CONTROL 0x04 + +/* - Chorus Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ +#define AUDIO_CHORUS_LEVEL_CONTROL 0x02 +#define AUDIO_SHORUS_RATE_CONTROL 0x03 +#define AUDIO_CHORUS_DEPTH_CONTROL 0x04 + +/* - Dynamic Range Compressor Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ +#define AUDIO_COMPRESSION_RATE_CONTROL 0x02 +#define AUDIO_MAX_AMPL_CONTROL 0x03 +#define AUDIO_THRESHOLD_CONTROL 0x04 +#define AUDIO_ATTACK_TIME_CONTROL 0x05 +#define AUDIO_RELEASE_TIME_CONTROL 0x06 + +/* Extension Unit Control Selectors */ +/* AUDIO_ENABLE_CONTROL 0x01 Common Selector */ + +/* Endpoint Control Selectors */ +#define AUDIO_SAMPLING_FREQ_CONTROL 0x01 +#define AUDIO_PITCH_CONTROL 0x02 + + +/* Audio Format Specific Control Selectors */ + +/* MPEG Control Selectors */ +#define AUDIO_MPEG_CONTROL_UNDEFINED 0x00 +#define AUDIO_MPEG_DUAL_CHANNEL_CONTROL 0x01 +#define AUDIO_MPEG_SECOND_STEREO_CONTROL 0x02 +#define AUDIO_MPEG_MULTILINGUAL_CONTROL 0x03 +#define AUDIO_MPEG_DYN_RANGE_CONTROL 0x04 +#define AUDIO_MPEG_SCALING_CONTROL 0x05 +#define AUDIO_MPEG_HILO_SCALING_CONTROL 0x06 + +/* AC-3 Control Selectors */ +#define AUDIO_AC3_CONTROL_UNDEFINED 0x00 +#define AUDIO_AC3_MODE_CONTROL 0x01 +#define AUDIO_AC3_DYN_RANGE_CONTROL 0x02 +#define AUDIO_AC3_SCALING_CONTROL 0x03 +#define AUDIO_AC3_HILO_SCALING_CONTROL 0x04 + + +/* Audio Format Types */ +#define AUDIO_FORMAT_TYPE_UNDEFINED 0x00 +#define AUDIO_FORMAT_TYPE_I 0x01 +#define AUDIO_FORMAT_TYPE_II 0x02 +#define AUDIO_FORMAT_TYPE_III 0x03 + + +/* Audio Format Type Descriptor Sizes */ +#define AUDIO_FORMAT_TYPE_I_DESC_SZ(n) 0x08+(n*3) +#define AUDIO_FORMAT_TYPE_II_DESC_SZ(n) 0x09+(n*3) +#define AUDIO_FORMAT_TYPE_III_DESC_SZ(n) 0x08+(n*3) +#define AUDIO_FORMAT_MPEG_DESC_SIZE 0x09 +#define AUDIO_FORMAT_AC3_DESC_SIZE 0x0A + + +/* Audio Data Format Codes */ + +/* Audio Data Format Type I Codes */ +#define AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0000 +#define AUDIO_FORMAT_PCM 0x0001 +#define AUDIO_FORMAT_PCM8 0x0002 +#define AUDIO_FORMAT_IEEE_FLOAT 0x0003 +#define AUDIO_FORMAT_ALAW 0x0004 +#define AUDIO_FORMAT_MULAW 0x0005 + +/* Audio Data Format Type II Codes */ +#define AUDIO_FORMAT_TYPE_II_UNDEFINED 0x1000 +#define AUDIO_FORMAT_MPEG 0x1001 +#define AUDIO_FORMAT_AC3 0x1002 + +/* Audio Data Format Type III Codes */ +#define AUDIO_FORMAT_TYPE_III_UNDEFINED 0x2000 +#define AUDIO_FORMAT_IEC1937_AC3 0x2001 +#define AUDIO_FORMAT_IEC1937_MPEG1_L1 0x2002 +#define AUDIO_FORMAT_IEC1937_MPEG1_L2_3 0x2003 +#define AUDIO_FORMAT_IEC1937_MPEG2_NOEXT 0x2003 +#define AUDIO_FORMAT_IEC1937_MPEG2_EXT 0x2004 +#define AUDIO_FORMAT_IEC1937_MPEG2_L1_LS 0x2005 +#define AUDIO_FORMAT_IEC1937_MPEG2_L2_3 0x2006 + + +/* Predefined Audio Channel Configuration Bits */ +#define AUDIO_CHANNEL_M 0x0000 /* Mono */ +#define AUDIO_CHANNEL_L 0x0001 /* Left Front */ +#define AUDIO_CHANNEL_R 0x0002 /* Right Front */ +#define AUDIO_CHANNEL_C 0x0004 /* Center Front */ +#define AUDIO_CHANNEL_LFE 0x0008 /* Low Freq. Enhance. */ +#define AUDIO_CHANNEL_LS 0x0010 /* Left Surround */ +#define AUDIO_CHANNEL_RS 0x0020 /* Right Surround */ +#define AUDIO_CHANNEL_LC 0x0040 /* Left of Center */ +#define AUDIO_CHANNEL_RC 0x0080 /* Right of Center */ +#define AUDIO_CHANNEL_S 0x0100 /* Surround */ +#define AUDIO_CHANNEL_SL 0x0200 /* Side Left */ +#define AUDIO_CHANNEL_SR 0x0400 /* Side Right */ +#define AUDIO_CHANNEL_T 0x0800 /* Top */ + + +/* Feature Unit Control Bits */ +#define AUDIO_CONTROL_MUTE 0x0001 +#define AUDIO_CONTROL_VOLUME 0x0002 +#define AUDIO_CONTROL_BASS 0x0004 +#define AUDIO_CONTROL_MID 0x0008 +#define AUDIO_CONTROL_TREBLE 0x0010 +#define AUDIO_CONTROL_GRAPHIC_EQUALIZER 0x0020 +#define AUDIO_CONTROL_AUTOMATIC_GAIN 0x0040 +#define AUDIO_CONTROL_DEALY 0x0080 +#define AUDIO_CONTROL_BASS_BOOST 0x0100 +#define AUDIO_CONTROL_LOUDNESS 0x0200 + +/* Processing Unit Control Bits: */ +#define AUDIO_CONTROL_ENABLE 0x0001 /* Common Bit */ +#define AUDIO_CONTROL_MODE_SELECT 0x0002 /* Common Bit */ + +/* - Up/Down-mix Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ +/* AUDIO_CONTROL_MODE_SELECT 0x0002 Common Bit */ + +/* - Dolby Prologic Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ +/* AUDIO_CONTROL_MODE_SELECT 0x0002 Common Bit */ + +/* - 3D Stereo Extender Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ +#define AUDIO_CONTROL_SPACIOUSNESS 0x0002 + +/* - Reverberation Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ +#define AUDIO_CONTROL_REVERB_TYPE 0x0002 +#define AUDIO_CONTROL_REVERB_LEVEL 0x0004 +#define AUDIO_CONTROL_REVERB_TIME 0x0008 +#define AUDIO_CONTROL_REVERB_FEEDBACK 0x0010 + +/* - Chorus Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ +#define AUDIO_CONTROL_CHORUS_LEVEL 0x0002 +#define AUDIO_CONTROL_SHORUS_RATE 0x0004 +#define AUDIO_CONTROL_CHORUS_DEPTH 0x0008 + +/* - Dynamic Range Compressor Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ +#define AUDIO_CONTROL_COMPRESSION_RATE 0x0002 +#define AUDIO_CONTROL_MAX_AMPL 0x0004 +#define AUDIO_CONTROL_THRESHOLD 0x0008 +#define AUDIO_CONTROL_ATTACK_TIME 0x0010 +#define AUDIO_CONTROL_RELEASE_TIME 0x0020 + +/* Extension Unit Control Bits */ +/* AUDIO_CONTROL_ENABLE 0x0001 Common Bit */ + +/* Endpoint Control Bits */ +#define AUDIO_CONTROL_SAMPLING_FREQ 0x01 +#define AUDIO_CONTROL_PITCH 0x02 +#define AUDIO_MAX_PACKETS_ONLY 0x80 + + +/* Audio Terminal Types */ + +/* USB Terminal Types */ +#define AUDIO_TERMINAL_USB_UNDEFINED 0x0100 +#define AUDIO_TERMINAL_USB_STREAMING 0x0101 +#define AUDIO_TERMINAL_USB_VENDOR_SPECIFIC 0x01FF + +/* Input Terminal Types */ +#define AUDIO_TERMINAL_INPUT_UNDEFINED 0x0200 +#define AUDIO_TERMINAL_MICROPHONE 0x0201 +#define AUDIO_TERMINAL_DESKTOP_MICROPHONE 0x0202 +#define AUDIO_TERMINAL_PERSONAL_MICROPHONE 0x0203 +#define AUDIO_TERMINAL_OMNI_DIR_MICROPHONE 0x0204 +#define AUDIO_TERMINAL_MICROPHONE_ARRAY 0x0205 +#define AUDIO_TERMINAL_PROCESSING_MIC_ARRAY 0x0206 + +/* Output Terminal Types */ +#define AUDIO_TERMINAL_OUTPUT_UNDEFINED 0x0300 +#define AUDIO_TERMINAL_SPEAKER 0x0301 +#define AUDIO_TERMINAL_HEADPHONES 0x0302 +#define AUDIO_TERMINAL_HEAD_MOUNTED_AUDIO 0x0303 +#define AUDIO_TERMINAL_DESKTOP_SPEAKER 0x0304 +#define AUDIO_TERMINAL_ROOM_SPEAKER 0x0305 +#define AUDIO_TERMINAL_COMMUNICATION_SPEAKER 0x0306 +#define AUDIO_TERMINAL_LOW_FREQ_SPEAKER 0x0307 + +/* Bi-directional Terminal Types */ +#define AUDIO_TERMINAL_BIDIRECTIONAL_UNDEFINED 0x0400 +#define AUDIO_TERMINAL_HANDSET 0x0401 +#define AUDIO_TERMINAL_HEAD_MOUNTED_HANDSET 0x0402 +#define AUDIO_TERMINAL_SPEAKERPHONE 0x0403 +#define AUDIO_TERMINAL_SPEAKERPHONE_ECHOSUPRESS 0x0404 +#define AUDIO_TERMINAL_SPEAKERPHONE_ECHOCANCEL 0x0405 + +/* Telephony Terminal Types */ +#define AUDIO_TERMINAL_TELEPHONY_UNDEFINED 0x0500 +#define AUDIO_TERMINAL_PHONE_LINE 0x0501 +#define AUDIO_TERMINAL_TELEPHONE 0x0502 +#define AUDIO_TERMINAL_DOWN_LINE_PHONE 0x0503 + +/* External Terminal Types */ +#define AUDIO_TERMINAL_EXTERNAL_UNDEFINED 0x0600 +#define AUDIO_TERMINAL_ANALOG_CONNECTOR 0x0601 +#define AUDIO_TERMINAL_DIGITAL_AUDIO_INTERFACE 0x0602 +#define AUDIO_TERMINAL_LINE_CONNECTOR 0x0603 +#define AUDIO_TERMINAL_LEGACY_AUDIO_CONNECTOR 0x0604 +#define AUDIO_TERMINAL_SPDIF_INTERFACE 0x0605 +#define AUDIO_TERMINAL_1394_DA_STREAM 0x0606 +#define AUDIO_TERMINAL_1394_DA_STREAM_TRACK 0x0607 + +/* Embedded Function Terminal Types */ +#define AUDIO_TERMINAL_EMBEDDED_UNDEFINED 0x0700 +#define AUDIO_TERMINAL_CALIBRATION_NOISE 0x0701 +#define AUDIO_TERMINAL_EQUALIZATION_NOISE 0x0702 +#define AUDIO_TERMINAL_CD_PLAYER 0x0703 +#define AUDIO_TERMINAL_DAT 0x0704 +#define AUDIO_TERMINAL_DCC 0x0705 +#define AUDIO_TERMINAL_MINI_DISK 0x0706 +#define AUDIO_TERMINAL_ANALOG_TAPE 0x0707 +#define AUDIO_TERMINAL_PHONOGRAPH 0x0708 +#define AUDIO_TERMINAL_VCR_AUDIO 0x0709 +#define AUDIO_TERMINAL_VIDEO_DISC_AUDIO 0x070A +#define AUDIO_TERMINAL_DVD_AUDIO 0x070B +#define AUDIO_TERMINAL_TV_TUNER_AUDIO 0x070C +#define AUDIO_TERMINAL_SATELLITE_RECEIVER_AUDIO 0x070D +#define AUDIO_TERMINAL_CABLE_TUNER_AUDIO 0x070E +#define AUDIO_TERMINAL_DSS_AUDIO 0x070F +#define AUDIO_TERMINAL_RADIO_RECEIVER 0x0710 +#define AUDIO_TERMINAL_RADIO_TRANSMITTER 0x0711 +#define AUDIO_TERMINAL_MULTI_TRACK_RECORDER 0x0712 +#define AUDIO_TERMINAL_SYNTHESIZER 0x0713 + + +#endif /* __AUDIO_H__ */ diff --git a/plugins/USBqemu/usb-mic/demo.h b/plugins/USBqemu/usb-mic/demo.h new file mode 100644 index 0000000000..f9293a49b2 --- /dev/null +++ b/plugins/USBqemu/usb-mic/demo.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------------------------- + * Name: DEMO.H + * Purpose: USB Audio Demo Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +/* Clock Definitions */ +#define CPU_CLOCK 60000000 /* CPU Clock */ +#define VPB_CLOCK (CPU_CLOCK/1) /* VPB Clock */ + +/* Audio Definitions */ +#define DATA_FREQ 32000 /* Audio Data Frequency */ +#define P_S 32 /* Packet Size */ +#if USB_DMA +#define P_C 4 /* Packet Count */ +#else +#define P_C 1 /* Packet Count */ +#endif +#define B_S (8*P_C*P_S) /* Buffer Size */ + +/* Push Button Definitions */ +#define PBINT 0x00004000 /* P0.14 */ + +/* LED Definitions */ +#define LEDMSK 0x00FF0000 /* P1.16..23 */ + +/* Audio Demo Variables */ +extern BYTE Mute; /* Mute State */ +extern DWORD Volume; /* Volume Level */ +extern WORD VolCur; /* Volume Current Value */ +extern DWORD InfoBuf[P_C]; /* Packet Info Buffer */ +extern short DataBuf[B_S]; /* Data Buffer */ +extern WORD DataOut; /* Data Out Index */ +extern WORD DataIn; /* Data In Index */ +extern BYTE DataRun; /* Data Stream Run State */ diff --git a/plugins/USBqemu/usb-mic/type.h b/plugins/USBqemu/usb-mic/type.h new file mode 100644 index 0000000000..4e6e17afcc --- /dev/null +++ b/plugins/USBqemu/usb-mic/type.h @@ -0,0 +1,22 @@ + +#ifndef __TYPE_H__ +#define __TYPE_H__ + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +//typedef unsigned int BOOL; + +#endif /* __TYPE_H__ */ diff --git a/plugins/USBqemu/usb-mic/usb-mic-dummy.cpp b/plugins/USBqemu/usb-mic/usb-mic-dummy.cpp new file mode 100644 index 0000000000..5c95ebfdfd --- /dev/null +++ b/plugins/USBqemu/usb-mic/usb-mic-dummy.cpp @@ -0,0 +1,431 @@ +/* + * QEMU USB HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "../qemu-usb/vl.h" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +#define USB_MOUSE 1 +#define USB_TABLET 2 + +#include "type.h" + +#include "usb.h" +#include "audio.h" +#include "usbcfg.h" +#include "usbdesc.h" + +typedef struct SINGSTARMICState { + USBDevice dev; + //nothing yet +} SINGSTARMICState; + +/* descriptor dumped from a real singstar MIC adapter */ +static const uint8_t singstar_mic_dev_descriptor[] = { + /* bLength */ 0x12, //(18) + /* bDescriptorType */ 0x01, //(1) + /* bcdUSB */ WBVAL(0x0110), //(272) + /* bDeviceClass */ 0x00, //(0) + /* bDeviceSubClass */ 0x00, //(0) + /* bDeviceProtocol */ 0x00, //(0) + /* bMaxPacketSize0 */ 0x08, //(8) + /* idVendor */ WBVAL(0x1415), //(5141) + /* idProduct */ WBVAL(0x0000), //(0) + /* bcdDevice */ WBVAL(0x0001), //(1) + /* iManufacturer */ 0x01, //(1) + /* iProduct */ 0x02, //(2) + /* iSerialNumber */ 0x00, //(0) + /* bNumConfigurations */ 0x01, //(1) + +}; + +static const uint8_t singstar_mic_config_descriptor[] = { + +/* Configuration 1 */ + 0x09, /* bLength */ + USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ + WBVAL(0x00b1), /* wTotalLength */ + 0x02, /* bNumInterfaces */ + 0x01, /* bConfigurationValue */ + 0x00, /* iConfiguration */ + USB_CONFIG_BUS_POWERED, /* bmAttributes */ + USB_CONFIG_POWER_MA(90), /* bMaxPower */ + +/* Interface 0, Alternate Setting 0, Audio Control */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x00, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ + +/* Audio Control Interface */ + AUDIO_CONTROL_INTERFACE_DESC_SZ(1), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ + WBVAL(0x0100), /* 1.00 */ /* bcdADC */ + WBVAL(0x0028), /* wTotalLength */ + 0x01, /* bInCollection */ + 0x01, /* baInterfaceNr */ + +/* Audio Input Terminal */ + AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ + 0x01, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_MICROPHONE), /* wTerminalType */ + 0x02, /* bAssocTerminal */ + 0x02, /* bNrChannels */ + WBVAL(AUDIO_CHANNEL_L + |AUDIO_CHANNEL_R), /* wChannelConfig */ + 0x00, /* iChannelNames */ + 0x00, /* iTerminal */ + +/* Audio Output Terminal */ + AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ + 0x02, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ + 0x01, /* bAssocTerminal */ + 0x03, /* bSourceID */ + 0x00, /* iTerminal */ + +/* Audio Feature Unit */ + AUDIO_FEATURE_UNIT_DESC_SZ(2,1), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ + 0x03, /* bUnitID */ + 0x01, /* bSourceID */ + 0x01, /* bControlSize */ + 0x01, /* bmaControls(0) */ + 0x02, /* bmaControls(1) */ + 0x02, /* bmaControls(2) */ + 0x00, /* iTerminal */ + +/* Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x01, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ + +/* Interface 1, Alternate Setting 1, Audio Streaming - Operational */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x01, /* bInterfaceNumber */ + 0x01, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ + +/* Audio Streaming Interface */ + AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x02, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + +/* Audio Type I Format */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x01, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ + +/* Endpoint - Standard Descriptor */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_OUT(0x81), /* bEndpointAddress */ + USB_ENDPOINT_TYPE_ISOCHRONOUS + | USB_ENDPOINT_SYNC_ASYNCHRONOUS, /* bmAttributes */ + WBVAL(0x0064), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ + +/* Endpoint - Audio Streaming */ + AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ + +/* Interface 1, Alternate Setting 2, Audio Streaming - ? */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x01, /* bInterfaceNumber */ + 0x02, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ + +/* Audio Streaming Interface */ + AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x02, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + +/* Audio Type I Format */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x02, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ + +/* Endpoint - Standard Descriptor */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_OUT(0x81), /* bEndpointAddress */ + USB_ENDPOINT_TYPE_ISOCHRONOUS + | USB_ENDPOINT_SYNC_ASYNCHRONOUS, /* bmAttributes */ + WBVAL(0x00c8), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ + +/* Endpoint - Audio Streaming */ + AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ + +/* Terminator */ + 0 /* bLength */ +}; + + +static void singstar_mic_handle_reset(USBDevice *dev) +{ + /* XXX: do it */ + return; +} + +static int singstar_mic_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + SINGSTARMICState *s = (SINGSTARMICState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, singstar_mic_dev_descriptor, + sizeof(singstar_mic_dev_descriptor)); + ret = sizeof(singstar_mic_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, singstar_mic_config_descriptor, + sizeof(singstar_mic_config_descriptor)); + ret = sizeof(singstar_mic_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "3X0420811"); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "EyeToy USB camera Namtai"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "PCSX2/QEMU"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + //switch(value >> 8) { + //((case 0x22: + // memcpy(data, qemu_mouse_hid_report_descriptor, + // sizeof(qemu_mouse_hid_report_descriptor)); + // ret = sizeof(qemu_mouse_hid_report_descriptor); + // break; + //default: + goto fail; + //} + break; + case GET_REPORT: + ret = 0; + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int singstar_mic_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + SINGSTARMICState *s = (SINGSTARMICState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + + +static void singstar_mic_handle_destroy(USBDevice *dev) +{ + SINGSTARMICState *s = (SINGSTARMICState *)dev; + + free(s); +} + +int singstar_mic_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + fprintf(stderr,"usb-singstar_mic: packet received with pid=%x, devaddr=%x, devep=%x and len=%x\n",pid,devaddr,devep,len); + return usb_generic_handle_packet(s,pid,devaddr,devep,data,len); +} + +USBDevice *singstar_mic_init() +{ + SINGSTARMICState *s; + + s = (SINGSTARMICState *)qemu_mallocz(sizeof(SINGSTARMICState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = singstar_mic_handle_packet; + s->dev.handle_reset = singstar_mic_handle_reset; + s->dev.handle_control = singstar_mic_handle_control; + s->dev.handle_data = singstar_mic_handle_data; + s->dev.handle_destroy = singstar_mic_handle_destroy; + + strncpy(s->dev.devname, "EyeToy USB camera Namtai", sizeof(s->dev.devname)); + + return (USBDevice *)s; + +} diff --git a/plugins/USBqemu/usb-mic/usb-mic.c b/plugins/USBqemu/usb-mic/usb-mic.c new file mode 100644 index 0000000000..80c69815c0 --- /dev/null +++ b/plugins/USBqemu/usb-mic/usb-mic.c @@ -0,0 +1,626 @@ +/* + * QEMU USB HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +#define USB_MOUSE 1 +#define USB_TABLET 2 + +#include "type.h" + +#include "usb.h" +#include "audio.h" +#include "usbcfg.h" +#include "usbdesc.h" + +/* mostly the same values as the Bochs USB Mouse device */ +static const uint8_t qemu_mic_dev_descriptor[] = { + sizeof(qemu_mic_dev_descriptor), /* bLength */ + 1, /* bDescriptorType */ + WBVAL(0x0110), /* 1.10 */ /* bcdUSB */ + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + USB_MAX_PACKET0, /* bMaxPacketSize0 */ + WBVAL(0xC251), /* idVendor */ + WBVAL(0x1304), /* idProduct */ + WBVAL(0x0100), /* 1.00 */ /* bcdDevice */ + 0x04, /* iManufacturer */ + 0x20, /* iProduct */ + 0x4A, /* iSerialNumber */ + 0x01 /* bNumConfigurations */ +}; + +static const uint8_t qemu_mic_config_descriptor[] = { +/* Configuration 1 */ + sizeof(qemu_mic_config_descriptor), /* bLength */ + USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ + WBVAL( /* wTotalLength */ + USB_CONFIGUARTION_DESC_SIZE + + USB_INTERFACE_DESC_SIZE + + AUDIO_CONTROL_INTERFACE_DESC_SZ(1) + + AUDIO_INPUT_TERMINAL_DESC_SIZE + + AUDIO_FEATURE_UNIT_DESC_SZ(1,1) + + AUDIO_OUTPUT_TERMINAL_DESC_SIZE + + USB_INTERFACE_DESC_SIZE + + USB_INTERFACE_DESC_SIZE + + AUDIO_STREAMING_INTERFACE_DESC_SIZE + + AUDIO_FORMAT_TYPE_I_DESC_SZ(1) + + AUDIO_STANDARD_ENDPOINT_DESC_SIZE + + AUDIO_STREAMING_ENDPOINT_DESC_SIZE + ), + 0x02, /* bNumInterfaces */ + 0x01, /* bConfigurationValue */ + 0x00, /* iConfiguration */ + USB_CONFIG_BUS_POWERED, /* bmAttributes */ + USB_CONFIG_POWER_MA(100), /* bMaxPower */ +/* Interface 0, Alternate Setting 0, Audio Control */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x00, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ +/* Audio Control Interface */ + AUDIO_CONTROL_INTERFACE_DESC_SZ(1), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ + WBVAL(0x0100), /* 1.00 */ /* bcdADC */ + WBVAL( /* wTotalLength */ + AUDIO_CONTROL_INTERFACE_DESC_SZ(1) + + AUDIO_INPUT_TERMINAL_DESC_SIZE + + AUDIO_FEATURE_UNIT_DESC_SZ(1,1) + + AUDIO_OUTPUT_TERMINAL_DESC_SIZE + ), + 0x01, /* bInCollection */ + 0x01, /* baInterfaceNr */ +/* Audio Input Terminal */ + AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ + 0x01, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ + 0x00, /* bAssocTerminal */ + 0x01, /* bNrChannels */ + WBVAL(AUDIO_CHANNEL_M), /* wChannelConfig */ + 0x00, /* iChannelNames */ + 0x00, /* iTerminal */ +/* Audio Feature Unit */ + AUDIO_FEATURE_UNIT_DESC_SZ(1,1), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ + 0x02, /* bUnitID */ + 0x01, /* bSourceID */ + 0x01, /* bControlSize */ + AUDIO_CONTROL_MUTE | + AUDIO_CONTROL_VOLUME, /* bmaControls(0) */ + 0x00, /* bmaControls(1) */ + 0x00, /* iTerminal */ +/* Audio Output Terminal */ + AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ + 0x03, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_SPEAKER), /* wTerminalType */ + 0x00, /* bAssocTerminal */ + 0x02, /* bSourceID */ + 0x00, /* iTerminal */ +/* Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x01, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ +/* Interface 1, Alternate Setting 1, Audio Streaming - Operational */ + USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x01, /* bInterfaceNumber */ + 0x01, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ +/* Audio Streaming Interface */ + AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x01, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ +/* Audio Type I Format */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(1), /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x01, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 16, /* bBitResolution */ + 0x01, /* bSamFreqType */ + B3VAL(32000), /* tSamFreq */ +/* Endpoint - Standard Descriptor */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_OUT(3), /* bEndpointAddress */ + USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */ + WBVAL(64), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ +/* Endpoint - Audio Streaming */ + AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x00, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ +/* Terminator */ + 0 /* bLength */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_mouse_hid_report_descriptor[] = { + 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, + 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, + 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, + 0xC0, 0xC0, +}; + +static const uint8_t qemu_tablet_hid_report_descriptor[] = { + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x01, /* Usage Mouse */ + 0xA1, 0x01, /* Collection Application */ + 0x09, 0x01, /* Usage Pointer */ + 0xA1, 0x00, /* Collection Physical */ + 0x05, 0x09, /* Usage Page Button */ + 0x19, 0x01, /* Usage Minimum Button 1 */ + 0x29, 0x03, /* Usage Maximum Button 3 */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x25, 0x01, /* Logical Maximum 1 */ + 0x95, 0x03, /* Report Count 3 */ + 0x75, 0x01, /* Report Size 1 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x01, /* Report Count 1 */ + 0x75, 0x05, /* Report Size 5 */ + 0x81, 0x01, /* Input (Cnst, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x30, /* Usage X */ + 0x09, 0x31, /* Usage Y */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */ + 0x35, 0x00, /* Physical Minimum 0 */ + 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */ + 0x75, 0x10, /* Report Size 16 */ + 0x95, 0x02, /* Report Count 2 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x38, /* Usage Wheel */ + 0x15, 0x81, /* Logical Minimum -127 */ + 0x25, 0x7F, /* Logical Maximum 127 */ + 0x35, 0x00, /* Physical Minimum 0 (same as logical) */ + 0x45, 0x00, /* Physical Maximum 0 (same as logical) */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x02, /* Input (Data, Var, Rel) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +static void usb_mouse_event(void *opaque, + int dx1, int dy1, int dz1, int buttons_state) +{ + USBMouseState *s = opaque; + + s->dx += dx1; + s->dy += dy1; + s->dz += dz1; + s->buttons_state = buttons_state; +} + +static void usb_tablet_event(void *opaque, + int x, int y, int dz, int buttons_state) +{ + USBMouseState *s = opaque; + + s->x = x; + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; +} + +static inline int int_clamp(int val, int vmin, int vmax) +{ + if (val < vmin) + return vmin; + else if (val > vmax) + return vmax; + else + return val; +} + +static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_mouse_event, s, 0); + s->mouse_grabbed = 1; + } + + dx = int_clamp(s->dx, -128, 127); + dy = int_clamp(s->dy, -128, 127); + dz = int_clamp(s->dz, -128, 127); + + s->dx -= dx; + s->dy -= dy; + s->dz -= dz; + + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = dx; + buf[2] = dy; + l = 3; + if (len >= 4) { + buf[3] = dz; + l = 4; + } + return l; +} + +static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_tablet_event, s, 1); + s->mouse_grabbed = 1; + } + + dz = int_clamp(s->dz, -128, 127); + s->dz -= dz; + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = s->x & 0xff; + buf[2] = s->x >> 8; + buf[3] = s->y & 0xff; + buf[4] = s->y >> 8; + buf[5] = dz; + l = 6; + + return l; +} + +static void usb_mouse_handle_reset(USBDevice *dev) +{ + USBMouseState *s = (USBMouseState *)dev; + + s->dx = 0; + s->dy = 0; + s->dz = 0; + s->x = 0; + s->y = 0; + s->buttons_state = 0; +} + +static int usb_mouse_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_mouse_dev_descriptor, + sizeof(qemu_mouse_dev_descriptor)); + ret = sizeof(qemu_mouse_dev_descriptor); + break; + case USB_DT_CONFIG: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_config_descriptor, + sizeof(qemu_mouse_config_descriptor)); + ret = sizeof(qemu_mouse_config_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_config_descriptor, + sizeof(qemu_tablet_config_descriptor)); + ret = sizeof(qemu_tablet_config_descriptor); + } + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "1"); + break; + case 2: + /* product description */ + if (s->kind == USB_MOUSE) + ret = set_usb_string(data, "QEMU USB Mouse"); + else if (s->kind == USB_TABLET) + ret = set_usb_string(data, "QEMU USB Tablet"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "PCSX2/QEMU"); + break; + case 4: + ret = set_usb_string(data, "HID Mouse"); + break; + case 5: + ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case 0x22: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); + ret = sizeof(qemu_mouse_hid_report_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, + sizeof(qemu_tablet_hid_report_descriptor)); + ret = sizeof(qemu_tablet_hid_report_descriptor); + } + break; + default: + goto fail; + } + break; + case GET_REPORT: + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, length); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, length); + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_mouse_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static void usb_mouse_handle_destroy(USBDevice *dev) +{ + USBMouseState *s = (USBMouseState *)dev; + + qemu_add_mouse_event_handler(NULL, NULL, 0); + free(s); +} + +USBDevice *usb_tablet_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->dev.handle_destroy = usb_mouse_handle_destroy; + s->kind = USB_TABLET; + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); + + return (USBDevice *)s; +} + +USBDevice *usb_mouse_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->dev.handle_destroy = usb_mouse_handle_destroy; + s->kind = USB_MOUSE; + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); + + return (USBDevice *)s; +} diff --git a/plugins/USBqemu/usb-mic/usb.h b/plugins/USBqemu/usb-mic/usb.h new file mode 100644 index 0000000000..727a14c696 --- /dev/null +++ b/plugins/USBqemu/usb-mic/usb.h @@ -0,0 +1,230 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USB.H + * Purpose: USB Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USB_H__ +#define __USB_H__ + + +#pragma pack(1) + + +typedef union { + WORD W; + struct { + BYTE L; + BYTE H; + } WB; +} WORD_BYTE; + + +/* bmRequestType.Dir */ +#define REQUEST_HOST_TO_DEVICE 0 +#define REQUEST_DEVICE_TO_HOST 1 + +/* bmRequestType.Type */ +#define REQUEST_STANDARD 0 +#define REQUEST_CLASS 1 +#define REQUEST_VENDOR 2 +#define REQUEST_RESERVED 3 + +/* bmRequestType.Recipient */ +#define REQUEST_TO_DEVICE 0 +#define REQUEST_TO_INTERFACE 1 +#define REQUEST_TO_ENDPOINT 2 +#define REQUEST_TO_OTHER 3 + +/* bmRequestType Definition */ +typedef union _REQUEST_TYPE { + struct _BM { + BYTE Recipient : 5; + BYTE Type : 2; + BYTE Dir : 1; + } BM; + BYTE B; +} REQUEST_TYPE; + +/* USB Standard Request Codes */ +#define USB_REQUEST_GET_STATUS 0 +#define USB_REQUEST_CLEAR_FEATURE 1 +#define USB_REQUEST_SET_FEATURE 3 +#define USB_REQUEST_SET_ADDRESS 5 +#define USB_REQUEST_GET_DESCRIPTOR 6 +#define USB_REQUEST_SET_DESCRIPTOR 7 +#define USB_REQUEST_GET_CONFIGURATION 8 +#define USB_REQUEST_SET_CONFIGURATION 9 +#define USB_REQUEST_GET_INTERFACE 10 +#define USB_REQUEST_SET_INTERFACE 11 +#define USB_REQUEST_SYNC_FRAME 12 + +/* USB GET_STATUS Bit Values */ +#define USB_GETSTATUS_SELF_POWERED 0x01 +#define USB_GETSTATUS_REMOTE_WAKEUP 0x02 +#define USB_GETSTATUS_ENDPOINT_STALL 0x01 + +/* USB Standard Feature selectors */ +#define USB_FEATURE_ENDPOINT_STALL 0 +#define USB_FEATURE_REMOTE_WAKEUP 1 + +/* USB Default Control Pipe Setup Packet */ +typedef struct _USB_SETUP_PACKET { + REQUEST_TYPE bmRequestType; + BYTE bRequest; + WORD_BYTE wValue; + WORD_BYTE wIndex; + WORD wLength; +} USB_SETUP_PACKET; + + +/* USB Descriptor Types */ +#define USB_DEVICE_DESCRIPTOR_TYPE 1 +#define USB_CONFIGURATION_DESCRIPTOR_TYPE 2 +#define USB_STRING_DESCRIPTOR_TYPE 3 +#define USB_INTERFACE_DESCRIPTOR_TYPE 4 +#define USB_ENDPOINT_DESCRIPTOR_TYPE 5 +#define USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE 6 +#define USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE 7 +#define USB_INTERFACE_POWER_DESCRIPTOR_TYPE 8 + +/* USB Device Classes */ +#define USB_DEVICE_CLASS_RESERVED 0x00 +#define USB_DEVICE_CLASS_AUDIO 0x01 +#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02 +#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03 +#define USB_DEVICE_CLASS_MONITOR 0x04 +#define USB_DEVICE_CLASS_PHYSICAL_INTERFACE 0x05 +#define USB_DEVICE_CLASS_POWER 0x06 +#define USB_DEVICE_CLASS_PRINTER 0x07 +#define USB_DEVICE_CLASS_STORAGE 0x08 +#define USB_DEVICE_CLASS_HUB 0x09 +#define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF + +/* bmAttributes in Configuration Descriptor */ +#define USB_CONFIG_POWERED_MASK 0xC0 +#define USB_CONFIG_BUS_POWERED 0x80 +#define USB_CONFIG_SELF_POWERED 0x40 +#define USB_CONFIG_REMOTE_WAKEUP 0x20 + +/* bMaxPower in Configuration Descriptor */ +#define USB_CONFIG_POWER_MA(mA) ((mA)/2) + +/* bEndpointAddress in Endpoint Descriptor */ +#define USB_ENDPOINT_DIRECTION_MASK 0x80 +#define USB_ENDPOINT_OUT(addr) ((addr) | 0x00) +#define USB_ENDPOINT_IN(addr) ((addr) | 0x80) + +/* bmAttributes in Endpoint Descriptor */ +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0x00 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_TYPE_BULK 0x02 +#define USB_ENDPOINT_TYPE_INTERRUPT 0x03 +#define USB_ENDPOINT_SYNC_MASK 0x0C +#define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION 0x00 +#define USB_ENDPOINT_SYNC_ASYNCHRONOUS 0x04 +#define USB_ENDPOINT_SYNC_ADAPTIVE 0x08 +#define USB_ENDPOINT_SYNC_SYNCHRONOUS 0x0C +#define USB_ENDPOINT_USAGE_MASK 0x30 +#define USB_ENDPOINT_USAGE_DATA 0x00 +#define USB_ENDPOINT_USAGE_FEEDBACK 0x10 +#define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK 0x20 +#define USB_ENDPOINT_USAGE_RESERVED 0x30 + +/* USB Standard Device Descriptor */ +typedef struct _USB_DEVICE_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; + WORD bcdUSB; + BYTE bDeviceClass; + BYTE bDeviceSubClass; + BYTE bDeviceProtocol; + BYTE bMaxPacketSize0; + WORD idVendor; + WORD idProduct; + WORD bcdDevice; + BYTE iManufacturer; + BYTE iProduct; + BYTE iSerialNumber; + BYTE bNumConfigurations; +} USB_DEVICE_DESCRIPTOR; + +/* USB 2.0 Device Qualifier Descriptor */ +typedef struct _USB_DEVICE_QUALIFIER_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; + WORD bcdUSB; + BYTE bDeviceClass; + BYTE bDeviceSubClass; + BYTE bDeviceProtocol; + BYTE bMaxPacketSize0; + BYTE bNumConfigurations; + BYTE bReserved; +} USB_DEVICE_QUALIFIER_DESCRIPTOR; + +/* USB Standard Configuration Descriptor */ +typedef struct _USB_CONFIGURATION_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; + WORD wTotalLength; + BYTE bNumInterfaces; + BYTE bConfigurationValue; + BYTE iConfiguration; + BYTE bmAttributes; + BYTE MaxPower; +} USB_CONFIGURATION_DESCRIPTOR; + +/* USB Standard Interface Descriptor */ +typedef struct _USB_INTERFACE_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; + BYTE bInterfaceNumber; + BYTE bAlternateSetting; + BYTE bNumEndpoints; + BYTE bInterfaceClass; + BYTE bInterfaceSubClass; + BYTE bInterfaceProtocol; + BYTE iInterface; +} USB_INTERFACE_DESCRIPTOR; + +/* USB Standard Endpoint Descriptor */ +typedef struct _USB_ENDPOINT_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; + BYTE bEndpointAddress; + BYTE bmAttributes; + WORD wMaxPacketSize; + BYTE bInterval; +} USB_ENDPOINT_DESCRIPTOR; + +/* USB String Descriptor */ +typedef struct _USB_STRING_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; + WORD bString/*[]*/; +} USB_STRING_DESCRIPTOR; + +/* USB Common Descriptor */ +typedef struct _USB_COMMON_DESCRIPTOR { + BYTE bLength; + BYTE bDescriptorType; +} USB_COMMON_DESCRIPTOR; + + +#pragma pack() + + +#endif /* __USB_H__ */ diff --git a/plugins/USBqemu/usb-mic/usbcfg.h b/plugins/USBqemu/usb-mic/usbcfg.h new file mode 100644 index 0000000000..fb425bcc0d --- /dev/null +++ b/plugins/USBqemu/usb-mic/usbcfg.h @@ -0,0 +1,161 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBCFG.H + * Purpose: USB Custom Configuration + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USBCFG_H__ +#define __USBCFG_H__ + + +/* +//*** <<< Use Configuration Wizard in Context Menu >>> *** +*/ + + +/* +// USB Configuration +// USB Power +// Default Power Setting +// <0=> Bus-powered +// <1=> Self-powered +// Max Number of Interfaces <1-256> +// Max Number of Endpoints <1-32> +// Max Endpoint 0 Packet Size +// <8=> 8 Bytes <16=> 16 Bytes <32=> 32 Bytes <64=> 64 Bytes +// DMA Transfer +// Use DMA for selected Endpoints +// Endpoint 0 Out +// Endpoint 0 In +// Endpoint 1 Out +// Endpoint 1 In +// Endpoint 2 Out +// Endpoint 2 In +// Endpoint 3 Out +// Endpoint 3 In +// Endpoint 4 Out +// Endpoint 4 In +// Endpoint 5 Out +// Endpoint 5 In +// Endpoint 6 Out +// Endpoint 6 In +// Endpoint 7 Out +// Endpoint 7 In +// Endpoint 8 Out +// Endpoint 8 In +// Endpoint 9 Out +// Endpoint 9 In +// Endpoint 10 Out +// Endpoint 10 In +// Endpoint 11 Out +// Endpoint 11 In +// Endpoint 12 Out +// Endpoint 12 In +// Endpoint 13 Out +// Endpoint 13 In +// Endpoint 14 Out +// Endpoint 14 In +// Endpoint 15 Out +// Endpoint 15 In +// +// +*/ + +#define USB_POWER 0 +#define USB_IF_NUM 4 +#define USB_EP_NUM 32 +#define USB_MAX_PACKET0 64 +#define USB_DMA 0 +#define USB_DMA_EP 0x00000040 + + +/* +// USB Event Handlers +// Device Events +// Power Event +// Reset Event +// Suspend Event +// Resume Event +// Remote Wakeup Event +// Start of Frame Event +// Error Event +// +// Endpoint Events +// Endpoint 0 Event +// Endpoint 1 Event +// Endpoint 2 Event +// Endpoint 3 Event +// Endpoint 4 Event +// Endpoint 5 Event +// Endpoint 6 Event +// Endpoint 7 Event +// Endpoint 8 Event +// Endpoint 9 Event +// Endpoint 10 Event +// Endpoint 11 Event +// Endpoint 12 Event +// Endpoint 13 Event +// Endpoint 14 Event +// Endpoint 15 Event +// +// USB Core Events +// Set Configuration Event +// Set Interface Event +// Set/Clear Feature Event +// +// +*/ + +#define USB_POWER_EVENT 0 +#define USB_RESET_EVENT 1 +#define USB_SUSPEND_EVENT 0 +#define USB_RESUME_EVENT 0 +#define USB_WAKEUP_EVENT 0 +#define USB_SOF_EVENT 1 +#define USB_ERROR_EVENT 0 +#define USB_EP_EVENT 0x0009 +#define USB_CONFIGURE_EVENT 0 +#define USB_INTERFACE_EVENT 0 +#define USB_FEATURE_EVENT 0 + + +/* +// USB Class Support +// Human Interface Device (HID) +// Interface Number <0-255> +// +// Mass Storage +// Interface Number <0-255> +// +// Audio Device +// Control Interface Number <0-255> +// Streaming Interface 1 Number <0-255> +// Streaming Interface 2 Number <0-255> +// +// +*/ + +#define USB_CLASS 1 +#define USB_HID 0 +#define USB_HID_IF_NUM 0 +#define USB_MSC 0 +#define USB_MSC_IF_NUM 0 +#define USB_AUDIO 1 +#define USB_ADC_CIF_NUM 0 +#define USB_ADC_SIF1_NUM 1 +#define USB_ADC_SIF2_NUM 2 + + +#endif /* __USBCFG_H__ */ diff --git a/plugins/USBqemu/usb-mic/usbcore.h b/plugins/USBqemu/usb-mic/usbcore.h new file mode 100644 index 0000000000..980afdfe16 --- /dev/null +++ b/plugins/USBqemu/usb-mic/usbcore.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBCORE.H + * Purpose: USB Core Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USBCORE_H__ +#define __USBCORE_H__ + + +/* USB Endpoint Data Structure */ +typedef struct _USB_EP_DATA { + BYTE *pData; + WORD Count; +} USB_EP_DATA; + +/* USB Core Global Variables */ +extern WORD USB_DeviceStatus; +extern BYTE USB_DeviceAddress; +extern BYTE USB_Configuration; +extern DWORD USB_EndPointMask; +extern DWORD USB_EndPointHalt; +extern BYTE USB_AltSetting[USB_IF_NUM]; + +/* USB Endpoint 0 Buffer */ +extern BYTE EP0Buf[USB_MAX_PACKET0]; + +/* USB Endpoint 0 Data Info */ +extern USB_EP_DATA EP0Data; + +/* USB Setup Packet */ +extern USB_SETUP_PACKET SetupPacket; + +/* USB Core Functions */ +extern void USB_ResetCore (void); + + +#endif /* __USBCORE_H__ */ diff --git a/plugins/USBqemu/usb-mic/usbdesc.h b/plugins/USBqemu/usb-mic/usbdesc.h new file mode 100644 index 0000000000..a896e86d2b --- /dev/null +++ b/plugins/USBqemu/usb-mic/usbdesc.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBDESC.C + * Purpose: USB Descriptors Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USBDESC_H__ +#define __USBDESC_H__ + + +#define WBVAL(x) (x & 0xFF),((x >> 8) & 0xFF) +#define B3VAL(x) (x & 0xFF),((x >> 8) & 0xFF),((x >> 16) & 0xFF) + +#define USB_DEVICE_DESC_SIZE (sizeof(USB_DEVICE_DESCRIPTOR)) +#define USB_CONFIGUARTION_DESC_SIZE (sizeof(USB_CONFIGURATION_DESCRIPTOR)) +#define USB_INTERFACE_DESC_SIZE (sizeof(USB_INTERFACE_DESCRIPTOR)) +#define USB_ENDPOINT_DESC_SIZE (sizeof(USB_ENDPOINT_DESCRIPTOR)) + +extern const BYTE USB_DeviceDescriptor[]; +extern const BYTE USB_ConfigDescriptor[]; +extern const BYTE USB_StringDescriptor[]; + + +#endif /* __USBDESC_H__ */ diff --git a/plugins/USBqemu/usb-mic/usbhw.h b/plugins/USBqemu/usb-mic/usbhw.h new file mode 100644 index 0000000000..e2a3aa9cf4 --- /dev/null +++ b/plugins/USBqemu/usb-mic/usbhw.h @@ -0,0 +1,107 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBHW.H + * Purpose: USB Hardware Layer Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USBHW_H__ +#define __USBHW_H__ + + +/* USB RAM Definitions */ +#define USB_RAM_ADR 0x7FD00000 /* USB RAM Start Address */ +#define USB_RAM_SZ 0x00002000 /* USB RAM Size (8kB) */ + +/* DMA Endpoint Descriptors */ +#define DD_NISO_CNT 16 /* Non-Iso EP DMA Descr. Count (max. 32) */ +#define DD_ISO_CNT 8 /* Iso EP DMA Descriptor Count (max. 32) */ +#define DD_NISO_SZ (DD_NISO_CNT * 16) /* Non-Iso DMA Descr. Size */ +#define DD_ISO_SZ (DD_ISO_CNT * 20) /* Iso DMA Descriptor Size */ +#define DD_NISO_ADR (USB_RAM_ADR + 128) /* Non-Iso DMA Descr. Address */ +#define DD_ISO_ADR (DD_NISO_ADR + DD_NISO_SZ) /* Iso DMA Descr. Address */ +#define DD_SZ (128 + DD_NISO_SZ + DD_ISO_SZ) /* Descr. Size */ + +/* DMA Buffer Memory Definitions */ +#define DMA_BUF_ADR (USB_RAM_ADR + DD_SZ) /* DMA Buffer Start Address */ +#define DMA_BUF_SZ (USB_RAM_SZ - DD_SZ) /* DMA Buffer Size */ + +/* USB Error Codes */ +#define USB_ERR_PID 0x0001 /* PID Error */ +#define USB_ERR_UEPKT 0x0002 /* Unexpected Packet */ +#define USB_ERR_DCRC 0x0004 /* Data CRC Error */ +#define USB_ERR_TIMOUT 0x0008 /* Bus Time-out Error */ +#define USB_ERR_EOP 0x0010 /* End of Packet Error */ +#define USB_ERR_B_OVRN 0x0020 /* Buffer Overrun */ +#define USB_ERR_BTSTF 0x0040 /* Bit Stuff Error */ +#define USB_ERR_TGL 0x0080 /* Toggle Bit Error */ + +/* USB DMA Status Codes */ +#define USB_DMA_INVALID 0x0000 /* DMA Invalid - Not Configured */ +#define USB_DMA_IDLE 0x0001 /* DMA Idle - Waiting for Trigger */ +#define USB_DMA_BUSY 0x0002 /* DMA Busy - Transfer in progress */ +#define USB_DMA_DONE 0x0003 /* DMA Transfer Done (no Errors)*/ +#define USB_DMA_OVER_RUN 0x0004 /* Data Over Run */ +#define USB_DMA_UNDER_RUN 0x0005 /* Data Under Run (Short Packet) */ +#define USB_DMA_ERROR 0x0006 /* Error */ +#define USB_DMA_UNKNOWN 0xFFFF /* Unknown State */ + +/* USB DMA Descriptor */ +typedef struct _USB_DMA_DESCRIPTOR { + DWORD BufAdr; /* DMA Buffer Address */ + WORD BufLen; /* DMA Buffer Length */ + WORD MaxSize; /* Maximum Packet Size */ + DWORD InfoAdr; /* Packet Info Memory Address */ + union { /* DMA Configuration */ + struct { + DWORD Link : 1; /* Link to existing Descriptors */ + DWORD IsoEP : 1; /* Isonchronous Endpoint */ + DWORD ATLE : 1; /* ATLE (Auto Transfer Length Extract) */ + DWORD Rsrvd : 5; /* Reserved */ + DWORD LenPos : 8; /* Length Position (ATLE) */ + } Type; + DWORD Val; + } Cfg; +} USB_DMA_DESCRIPTOR; + +/* USB Hardware Functions */ +extern void USB_Init (void); +extern void USB_Connect (BOOL con); +extern void USB_Reset (void); +extern void USB_Suspend (void); +extern void USB_Resume (void); +extern void USB_WakeUp (void); +extern void USB_WakeUpCfg (BOOL cfg); +extern void USB_SetAddress (DWORD adr); +extern void USB_Configure (BOOL cfg); +extern void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR *pEPD); +extern void USB_DirCtrlEP (DWORD dir); +extern void USB_EnableEP (DWORD EPNum); +extern void USB_DisableEP (DWORD EPNum); +extern void USB_ResetEP (DWORD EPNum); +extern void USB_SetStallEP (DWORD EPNum); +extern void USB_ClrStallEP (DWORD EPNum); +extern DWORD USB_ReadEP (DWORD EPNum, BYTE *pData); +extern DWORD USB_WriteEP (DWORD EPNum, BYTE *pData, DWORD cnt); +extern BOOL USB_DMA_Setup (DWORD EPNum, USB_DMA_DESCRIPTOR *pDD); +extern void USB_DMA_Enable (DWORD EPNum); +extern void USB_DMA_Disable(DWORD EPNum); +extern DWORD USB_DMA_Status (DWORD EPNum); +extern DWORD USB_DMA_BufAdr (DWORD EPNum); +extern DWORD USB_DMA_BufCnt (DWORD EPNum); +extern DWORD USB_GetFrame (void); +extern void USB_ISR (void) __irq; + + +#endif /* __USBHW_H__ */ diff --git a/plugins/USBqemu/usb-mic/usbreg.h b/plugins/USBqemu/usb-mic/usbreg.h new file mode 100644 index 0000000000..6dd2ff8b3c --- /dev/null +++ b/plugins/USBqemu/usb-mic/usbreg.h @@ -0,0 +1,195 @@ +/***********************************************************************/ +/* This file is part of the uVision/ARM development tools */ +/* Copyright KEIL ELEKTRONIK GmbH 2002-2006 */ +/***********************************************************************/ +/* */ +/* USBREG.H: Header file for Philips LPC214X USB */ +/* */ +/***********************************************************************/ + +#ifndef __USBREG_H +#define __USBREG_H + + +#define SCB_BASE_ADDR 0xE01FC000 /* System Control Block */ + +/* PLL48 Registers */ +#define PLL48CON (*(volatile unsigned int*)(SCB_BASE_ADDR + 0xA0)) +#define PLL48CFG (*(volatile unsigned int*)(SCB_BASE_ADDR + 0xA4)) +#define PLL48STAT (*(volatile unsigned int*)(SCB_BASE_ADDR + 0xA8)) +#define PLL48FEED (*(volatile unsigned int*)(SCB_BASE_ADDR + 0xAC)) + +/* PLL48 Bit Definitions */ +#define PLLCON_PLLE (1<<0) /* PLL Enable */ +#define PLLCON_PLLC (1<<1) /* PLL Connect */ +#define PLLCFG_MSEL (0x1F<<0) /* PLL Multiplier */ +#define PLLCFG_PSEL (0x03<<5) /* PLL Divider */ +#define PLLSTAT_PLOCK (1<<10) /* PLL Lock Status */ + + +#define USB_BASE_ADDR 0xE0090000 /* USB Base Address */ + +/* Device Interrupt Registers */ +#define DEV_INT_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0x00)) +#define DEV_INT_EN (*(volatile unsigned int*)(USB_BASE_ADDR + 0x04)) +#define DEV_INT_CLR (*(volatile unsigned int*)(USB_BASE_ADDR + 0x08)) +#define DEV_INT_SET (*(volatile unsigned int*)(USB_BASE_ADDR + 0x0C)) +#define DEV_INT_PRIO (*(volatile unsigned int*)(USB_BASE_ADDR + 0x2C)) + +/* Endpoint Interrupt Registers */ +#define EP_INT_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0x30)) +#define EP_INT_EN (*(volatile unsigned int*)(USB_BASE_ADDR + 0x34)) +#define EP_INT_CLR (*(volatile unsigned int*)(USB_BASE_ADDR + 0x38)) +#define EP_INT_SET (*(volatile unsigned int*)(USB_BASE_ADDR + 0x3C)) +#define EP_INT_PRIO (*(volatile unsigned int*)(USB_BASE_ADDR + 0x40)) + +/* Endpoint Realization Registers */ +#define REALIZE_EP (*(volatile unsigned int*)(USB_BASE_ADDR + 0x44)) +#define EP_INDEX (*(volatile unsigned int*)(USB_BASE_ADDR + 0x48)) +#define MAXPACKET_SIZE (*(volatile unsigned int*)(USB_BASE_ADDR + 0x4C)) + +/* Command Reagisters */ +#define CMD_CODE (*(volatile unsigned int*)(USB_BASE_ADDR + 0x10)) +#define CMD_DATA (*(volatile unsigned int*)(USB_BASE_ADDR + 0x14)) + +/* Data Transfer Registers */ +#define RX_DATA (*(volatile unsigned int*)(USB_BASE_ADDR + 0x18)) +#define TX_DATA (*(volatile unsigned int*)(USB_BASE_ADDR + 0x1C)) +#define RX_PLENGTH (*(volatile unsigned int*)(USB_BASE_ADDR + 0x20)) +#define TX_PLENGTH (*(volatile unsigned int*)(USB_BASE_ADDR + 0x24)) +#define USB_CTRL (*(volatile unsigned int*)(USB_BASE_ADDR + 0x28)) + +/* System Register */ +#define DC_REVISION (*(volatile unsigned int*)(USB_BASE_ADDR + 0x7C)) + +/* DMA Registers */ +#define DMA_REQ_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0x50)) +#define DMA_REQ_CLR (*(volatile unsigned int*)(USB_BASE_ADDR + 0x54)) +#define DMA_REQ_SET (*(volatile unsigned int*)(USB_BASE_ADDR + 0x58)) +#define UDCA_HEAD (*(volatile unsigned int*)(USB_BASE_ADDR + 0x80)) +#define EP_DMA_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0x84)) +#define EP_DMA_EN (*(volatile unsigned int*)(USB_BASE_ADDR + 0x88)) +#define EP_DMA_DIS (*(volatile unsigned int*)(USB_BASE_ADDR + 0x8C)) +#define DMA_INT_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0x90)) +#define DMA_INT_EN (*(volatile unsigned int*)(USB_BASE_ADDR + 0x94)) +#define EOT_INT_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0xA0)) +#define EOT_INT_CLR (*(volatile unsigned int*)(USB_BASE_ADDR + 0xA4)) +#define EOT_INT_SET (*(volatile unsigned int*)(USB_BASE_ADDR + 0xA8)) +#define NDD_REQ_INT_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0xAC)) +#define NDD_REQ_INT_CLR (*(volatile unsigned int*)(USB_BASE_ADDR + 0xB0)) +#define NDD_REQ_INT_SET (*(volatile unsigned int*)(USB_BASE_ADDR + 0xB4)) +#define SYS_ERR_INT_STAT (*(volatile unsigned int*)(USB_BASE_ADDR + 0xB8)) +#define SYS_ERR_INT_CLR (*(volatile unsigned int*)(USB_BASE_ADDR + 0xBC)) +#define SYS_ERR_INT_SET (*(volatile unsigned int*)(USB_BASE_ADDR + 0xC0)) +#define MODULE_ID (*(volatile unsigned int*)(USB_BASE_ADDR + 0xFC)) + + +/* Device Interrupt Bit Definitions */ +#define FRAME_INT 0x00000001 +#define EP_FAST_INT 0x00000002 +#define EP_SLOW_INT 0x00000004 +#define DEV_STAT_INT 0x00000008 +#define CCEMTY_INT 0x00000010 +#define CDFULL_INT 0x00000020 +#define RxENDPKT_INT 0x00000040 +#define TxENDPKT_INT 0x00000080 +#define EP_RLZED_INT 0x00000100 +#define ERR_INT 0x00000200 + +/* Rx & Tx Packet Length Definitions */ +#define PKT_LNGTH_MASK 0x000003FF +#define PKT_DV 0x00000400 +#define PKT_RDY 0x00000800 + +/* USB Control Definitions */ +#define CTRL_RD_EN 0x00000001 +#define CTRL_WR_EN 0x00000002 + +/* Command Codes */ +#define CMD_SET_ADDR 0x00D00500 +#define CMD_CFG_DEV 0x00D80500 +#define CMD_SET_MODE 0x00F30500 +#define CMD_RD_FRAME 0x00F50500 +#define DAT_RD_FRAME 0x00F50200 +#define CMD_RD_TEST 0x00FD0500 +#define DAT_RD_TEST 0x00FD0200 +#define CMD_SET_DEV_STAT 0x00FE0500 +#define CMD_GET_DEV_STAT 0x00FE0500 +#define DAT_GET_DEV_STAT 0x00FE0200 +#define CMD_GET_ERR_CODE 0x00FF0500 +#define DAT_GET_ERR_CODE 0x00FF0200 +#define CMD_RD_ERR_STAT 0x00FB0500 +#define DAT_RD_ERR_STAT 0x00FB0200 +#define DAT_WR_BYTE(x) (0x00000100 | ((x) << 16)) +#define CMD_SEL_EP(x) (0x00000500 | ((x) << 16)) +#define DAT_SEL_EP(x) (0x00000200 | ((x) << 16)) +#define CMD_SEL_EP_CLRI(x) (0x00400500 | ((x) << 16)) +#define DAT_SEL_EP_CLRI(x) (0x00400200 | ((x) << 16)) +#define CMD_SET_EP_STAT(x) (0x00400500 | ((x) << 16)) +#define CMD_CLR_BUF 0x00F20500 +#define DAT_CLR_BUF 0x00F20200 +#define CMD_VALID_BUF 0x00FA0500 + +/* Device Address Register Definitions */ +#define DEV_ADDR_MASK 0x7F +#define DEV_EN 0x80 + +/* Device Configure Register Definitions */ +#define CONF_DVICE 0x01 + +/* Device Mode Register Definitions */ +#define AP_CLK 0x01 +#define INAK_CI 0x02 +#define INAK_CO 0x04 +#define INAK_II 0x08 +#define INAK_IO 0x10 +#define INAK_BI 0x20 +#define INAK_BO 0x40 + +/* Device Status Register Definitions */ +#define DEV_CON 0x01 +#define DEV_CON_CH 0x02 +#define DEV_SUS 0x04 +#define DEV_SUS_CH 0x08 +#define DEV_RST 0x10 + +/* Error Code Register Definitions */ +#define ERR_EC_MASK 0x0F +#define ERR_EA 0x10 + +/* Error Status Register Definitions */ +#define ERR_PID 0x01 +#define ERR_UEPKT 0x02 +#define ERR_DCRC 0x04 +#define ERR_TIMOUT 0x08 +#define ERR_EOP 0x10 +#define ERR_B_OVRN 0x20 +#define ERR_BTSTF 0x40 +#define ERR_TGL 0x80 + +/* Endpoint Select Register Definitions */ +#define EP_SEL_F 0x01 +#define EP_SEL_ST 0x02 +#define EP_SEL_STP 0x04 +#define EP_SEL_PO 0x08 +#define EP_SEL_EPN 0x10 +#define EP_SEL_B_1_FULL 0x20 +#define EP_SEL_B_2_FULL 0x40 + +/* Endpoint Status Register Definitions */ +#define EP_STAT_ST 0x01 +#define EP_STAT_DA 0x20 +#define EP_STAT_RF_MO 0x40 +#define EP_STAT_CND_ST 0x80 + +/* Clear Buffer Register Definitions */ +#define CLR_BUF_PO 0x01 + + +/* DMA Interrupt Bit Definitions */ +#define EOT_INT 0x01 +#define NDD_REQ_INT 0x02 +#define SYS_ERR_INT 0x04 + + +#endif /* __USBREG_H */ diff --git a/plugins/USBqemu/usb-mic/usbuser.h b/plugins/USBqemu/usb-mic/usbuser.h new file mode 100644 index 0000000000..2307625934 --- /dev/null +++ b/plugins/USBqemu/usb-mic/usbuser.h @@ -0,0 +1,74 @@ +/*---------------------------------------------------------------------------- + * U S B - K e r n e l + *---------------------------------------------------------------------------- + * Name: USBUSER.H + * Purpose: USB Custom User Definitions + * Version: V1.10 + *---------------------------------------------------------------------------- + * This software is supplied "AS IS" without any warranties, express, + * implied or statutory, including but not limited to the implied + * warranties of fitness for purpose, satisfactory quality and + * noninfringement. Keil extends you a royalty-free right to reproduce + * and distribute executable files created using this software for use + * on Philips LPC2xxx microcontroller devices only. Nothing else gives + * you the right to use this software. + * + * Copyright (c) 2005-2006 Keil Software. + *---------------------------------------------------------------------------*/ + +#ifndef __USBUSER_H__ +#define __USBUSER_H__ + + +/* USB Device Events Callback Functions */ +extern void USB_Power_Event (BOOL power); +extern void USB_Reset_Event (void); +extern void USB_Suspend_Event (void); +extern void USB_Resume_Event (void); +extern void USB_WakeUp_Event (void); +extern void USB_SOF_Event (void); +extern void USB_Error_Event (DWORD error); + +/* USB Endpoint Callback Events */ +#define USB_EVT_SETUP 1 /* Setup Packet */ +#define USB_EVT_OUT 2 /* OUT Packet */ +#define USB_EVT_IN 3 /* IN Packet */ +#define USB_EVT_OUT_NAK 4 /* OUT Packet - Not Acknowledged */ +#define USB_EVT_IN_NAK 5 /* IN Packet - Not Acknowledged */ +#define USB_EVT_OUT_STALL 6 /* OUT Packet - Stalled */ +#define USB_EVT_IN_STALL 7 /* IN Packet - Stalled */ +#define USB_EVT_OUT_DMA_EOT 8 /* DMA OUT EP - End of Transfer */ +#define USB_EVT_IN_DMA_EOT 9 /* DMA IN EP - End of Transfer */ +#define USB_EVT_OUT_DMA_NDR 10 /* DMA OUT EP - New Descriptor Request */ +#define USB_EVT_IN_DMA_NDR 11 /* DMA IN EP - New Descriptor Request */ +#define USB_EVT_OUT_DMA_ERR 12 /* DMA OUT EP - Error */ +#define USB_EVT_IN_DMA_ERR 13 /* DMA IN EP - Error */ + +/* USB Endpoint Events Callback Pointers */ +extern void (* const USB_P_EP[16])(DWORD event); + +/* USB Endpoint Events Callback Functions */ +extern void USB_EndPoint0 (DWORD event); +extern void USB_EndPoint1 (DWORD event); +extern void USB_EndPoint2 (DWORD event); +extern void USB_EndPoint3 (DWORD event); +extern void USB_EndPoint4 (DWORD event); +extern void USB_EndPoint5 (DWORD event); +extern void USB_EndPoint6 (DWORD event); +extern void USB_EndPoint7 (DWORD event); +extern void USB_EndPoint8 (DWORD event); +extern void USB_EndPoint9 (DWORD event); +extern void USB_EndPoint10 (DWORD event); +extern void USB_EndPoint11 (DWORD event); +extern void USB_EndPoint12 (DWORD event); +extern void USB_EndPoint13 (DWORD event); +extern void USB_EndPoint14 (DWORD event); +extern void USB_EndPoint15 (DWORD event); + +/* USB Core Events Callback Functions */ +extern void USB_Configure_Event (void); +extern void USB_Interface_Event (void); +extern void USB_Feature_Event (void); + + +#endif /* __USBUSER_H__ */