USB: beginning to add back initial references

This commit is contained in:
Gauvain 'GovanifY' Roussel-Tarbouriech 2020-11-02 12:00:16 +01:00 committed by refractionpcsx2
parent 9dd0ef681d
commit 8f8a83a038
169 changed files with 412303 additions and 0 deletions

1
3rdparty/libsamplerate/AUTHORS vendored Normal file
View File

@ -0,0 +1 @@
Erik de Castro Lopo <erikd@mega-nerd.com>

25
3rdparty/libsamplerate/COPYING vendored Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2012-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

40
3rdparty/libsamplerate/NEWS vendored Normal file
View File

@ -0,0 +1,40 @@
Version 0.1.9 (2016-09-23)
* Relicense under 2 clause BSD license.
* Minor bug fixes and upates.
Version 0.1.8 (2011-08-15)
* Minor bug fixes and upates.
Version 0.1.7 (2009-02-14)
* Fix a segfault which occurs when memcpy is passed a bad length parameter.
* Fix compilation under MSVC.
Version 0.1.6 (2009-01-27)
* Minor bug fix in test suite (account for rounding error on x86_64).
Version 0.1.5 (2009-01-11)
* Optimisation resulting dramatic throughput improvements.
Version 0.1.4 (2008-07-02)
* Fix bug which causes a segfault with extremely low conversion ratios.
Version 0.1.3 (2008-03-23)
* Huge improvement to the quality of conversion with the
SRC_SINC_MEDIUM_QUALITY and SRC_SINC_BEST_QUALITY converters.
* Minor bug fixes.
Version 0.1.2 (2004-09-12)
* Fixed where callback based API wasn't being reset properly.
* Minor bug fixes.
Version 0.1.1 (2004-07-17)
* Fixed bug in callback based API.
* Fixed a bug brought to light by aggressive optimisations of gcc-3.4.
* Minor bug fixes.
Version 0.1.0 (2004-03-14)
* Added callback based API.
* Added a pair of functions for doing short to float and float to short
conversions on an arrays of data.
* Many minor bug fixes.

50
3rdparty/libsamplerate/README vendored Normal file
View File

@ -0,0 +1,50 @@
This is libsamplerate, 0.1.9
libsamplerate (also known as Secret Rabbit Code) is a library for
perfroming sample rate conversion of audio data.
The src/ directory contains the source code for library itself.
The doc/ directory contains the libsamplerate documentation.
The examples/ directory contains examples of how to write code using
libsamplerate.
The tests/ directory contains programs which link against
libsamplerate and test its functionality.
The Win32/ directory contains files and documentation to allow
libsamplerate to compile under Win32 with the Microsoft Visual C++
compiler.
Win32
-----
There are detailed instructions for building libsamplerate on Win32
in the file
doc/win32.html
MacOSX
------
Building on MacOSX should be the same as building it on any other
Unix.
OTHER PLATFORMS
---------------
To compile libsamplerate on platforms which have a Bourne Shell compatible
shell, an ANSI C compiler and a make utility should require no more that
the following three commands :
./configure
make
make install
CONTACTS
--------
libsamplerate was written by Erik de Castro Lopo (erikd AT mega-nerd DOT com).
The libsamplerate home page is at :
http://www.mega-nerd.com/libsamplerate/

160
3rdparty/libsamplerate/common.h vendored Normal file
View File

@ -0,0 +1,160 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
#ifndef COMMON_H_INCLUDED
#define COMMON_H_INCLUDED
#ifdef HAVE_STDINT_H
#include <stdint.h>
#elif (SIZEOF_INT == 4)
typedef int int32_t ;
#elif (SIZEOF_LONG == 4)
typedef long int32_t ;
#endif
#define SRC_MAX_RATIO 256
#define SRC_MAX_RATIO_STR "256"
#define SRC_MIN_RATIO_DIFF (1e-20)
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0])))
#define OFFSETOF(type,member) ((int) (&((type*) 0)->member))
#define MAKE_MAGIC(a,b,c,d,e,f) ((a) + ((b) << 4) + ((c) << 8) + ((d) << 12) + ((e) << 16) + ((f) << 20))
/*
** Inspiration : http://sourcefrog.net/weblog/software/languages/C/unused.html
*/
#ifdef UNUSED
#elif defined (__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
#elif defined (__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
#ifdef __GNUC__
# define WARN_UNUSED __attribute__ ((warn_unused_result))
#else
# define WARN_UNUSED
#endif
#include "samplerate.h"
enum
{ SRC_FALSE = 0,
SRC_TRUE = 1,
SRC_MODE_PROCESS = 555,
SRC_MODE_CALLBACK = 556
} ;
enum
{ SRC_ERR_NO_ERROR = 0,
SRC_ERR_MALLOC_FAILED,
SRC_ERR_BAD_STATE,
SRC_ERR_BAD_DATA,
SRC_ERR_BAD_DATA_PTR,
SRC_ERR_NO_PRIVATE,
SRC_ERR_BAD_SRC_RATIO,
SRC_ERR_BAD_PROC_PTR,
SRC_ERR_SHIFT_BITS,
SRC_ERR_FILTER_LEN,
SRC_ERR_BAD_CONVERTER,
SRC_ERR_BAD_CHANNEL_COUNT,
SRC_ERR_SINC_BAD_BUFFER_LEN,
SRC_ERR_SIZE_INCOMPATIBILITY,
SRC_ERR_BAD_PRIV_PTR,
SRC_ERR_BAD_SINC_STATE,
SRC_ERR_DATA_OVERLAP,
SRC_ERR_BAD_CALLBACK,
SRC_ERR_BAD_MODE,
SRC_ERR_NULL_CALLBACK,
SRC_ERR_NO_VARIABLE_RATIO,
SRC_ERR_SINC_PREPARE_DATA_BAD_LEN,
SRC_ERR_BAD_INTERNAL_STATE,
/* This must be the last error number. */
SRC_ERR_MAX_ERROR
} ;
typedef struct SRC_PRIVATE_tag
{ double last_ratio, last_position ;
int error ;
int channels ;
/* SRC_MODE_PROCESS or SRC_MODE_CALLBACK */
int mode ;
/* Pointer to data to converter specific data. */
void *private_data ;
/* Varispeed process function. */
int (*vari_process) (struct SRC_PRIVATE_tag *psrc, SRC_DATA *data) ;
/* Constant speed process function. */
int (*const_process) (struct SRC_PRIVATE_tag *psrc, SRC_DATA *data) ;
/* State reset. */
void (*reset) (struct SRC_PRIVATE_tag *psrc) ;
/* Data specific to SRC_MODE_CALLBACK. */
src_callback_t callback_func ;
void *user_callback_data ;
long saved_frames ;
const float *saved_data ;
} SRC_PRIVATE ;
/* In src_sinc.c */
const char* sinc_get_name (int src_enum) ;
const char* sinc_get_description (int src_enum) ;
int sinc_set_converter (SRC_PRIVATE *psrc, int src_enum) ;
/* In src_linear.c */
const char* linear_get_name (int src_enum) ;
const char* linear_get_description (int src_enum) ;
int linear_set_converter (SRC_PRIVATE *psrc, int src_enum) ;
/* In src_zoh.c */
const char* zoh_get_name (int src_enum) ;
const char* zoh_get_description (int src_enum) ;
int zoh_set_converter (SRC_PRIVATE *psrc, int src_enum) ;
/*----------------------------------------------------------
** Common static inline functions.
*/
static inline double
fmod_one (double x)
{ double res ;
res = x - lrint (x) ;
if (res < 0.0)
return res + 1.0 ;
return res ;
} /* fmod_one */
static inline int
is_bad_src_ratio (double ratio)
{ return (ratio < (1.0 / SRC_MAX_RATIO) || ratio > (1.0 * SRC_MAX_RATIO)) ;
} /* is_bad_src_ratio */
#endif /* COMMON_H_INCLUDED */

207
3rdparty/libsamplerate/config.h vendored Normal file
View File

@ -0,0 +1,207 @@
/*
** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
**
** 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.
*/
/*
** This is the Win32 specific config.h header file.
**
** On Unix (including MacOSX), this header file is automatically generated
** during the configure process while on Win32 this has to be hand edited
** to keep it up to date.
**
** This is also a good file to add Win32 specific things.
*/
/*
** MSVC++ assumes that all floating point constants without a trailing
** letter 'f' are double precision.
**
** If this assumption is incorrect and one of these floating point constants
** is assigned to a float variable MSVC++ generates a warning.
**
** Since there are currently about 25000 of these warnings generated in
** src/src_sinc.c this slows down compile times considerably. The
** following #pragma disables the warning.
*/
#pragma warning(disable: 4305)
/*----------------------------------------------------------------------------
** Normal #defines follow.
*/
/* Set to 1 if the compile is GNU GCC. */
#define COMPILER_IS_GCC 0
/* Target processor clips on negative float to int conversion. */
#define CPU_CLIPS_NEGATIVE 0
/* Target processor clips on positive float to int conversion. */
#define CPU_CLIPS_POSITIVE 0
/* Target processor is big endian. */
#define CPU_IS_BIG_ENDIAN 0
/* Target processor is little endian. */
#define CPU_IS_LITTLE_ENDIAN 1
/* Set to 1 to enable debugging. */
#define ENABLE_DEBUG 0
/* Major version of GCC or 3 otherwise. */
/* #undef GCC_MAJOR_VERSION */
/* Define to 1 if you have the `alarm' function. */
/* #undef HAVE_ALARM */
/* Define to 1 if you have the `calloc' function. */
#define HAVE_CALLOC 1
/* Define to 1 if you have the `ceil' function. */
#define HAVE_CEIL 1
/* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
/* Set to 1 if you have libfftw3. */
/* #undef HAVE_FFTW3 */
/* Define to 1 if you have the `floor' function. */
#define HAVE_FLOOR 1
/* Define to 1 if you have the `fmod' function. */
#define HAVE_FMOD 1
/* Define to 1 if you have the `free' function. */
#define HAVE_FREE 1
/* Define to 1 if you have the <inttypes.h> header file. */
/* #undef HAVE_INTTYPES_H */
/* Define to 1 if you have the `m' library (-lm). */
/* #undef HAVE_LIBM */
/* Define if you have C99's lrint function. */
/* #undef HAVE_LRINT */
/* Define if you have C99's lrintf function. */
/* #undef HAVE_LRINTF */
/* Define to 1 if you have the `malloc' function. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the `memcpy' function. */
#define HAVE_MEMCPY 1
/* Define to 1 if you have the `memmove' function. */
#define HAVE_MEMMOVE 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define if you have signal SIGALRM. */
/* #undef HAVE_SIGALRM */
/* Define to 1 if you have the `signal' function. */
/* #undef HAVE_SIGNAL */
/* Set to 1 if you have libsndfile. */
#define HAVE_SNDFILE 1
/* Define to 1 if you have the <stdint.h> header file. */
/* #undef HAVE_STDINT_H */
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/times.h> header file. */
/* #undef HAVE_SYS_TIMES_H */
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O */
/* Set to 1 if compiling for Win32 */
#define OS_IS_WIN32 1
/* Name of package */
#define PACKAGE "libsamplerate"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "erikd@mega-nerd.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "libsamplerate"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libsamplerate 0.1.9"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libsamplerate"
/* Define to the home page for this package. */
#define PACKAGE_URL "http://www.mega-nerd.com/libsamplerate/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.1.9"
/* The size of `double', as computed by sizeof. */
#define SIZEOF_DOUBLE 8
/* The size of `float', as computed by sizeof. */
#define SIZEOF_FLOAT 4
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG 4
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "0.1.9"
/* Extra Win32 hacks. */
/*
** Microsoft's compiler still does not support the 1999 ISO C Standard
** which includes 'inline'.
*/
#define inline __inline

148
3rdparty/libsamplerate/config.h.in vendored Normal file
View File

@ -0,0 +1,148 @@
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Set to 1 if the compile is GNU GCC. */
#undef COMPILER_IS_GCC
/* Target processor clips on negative float to int conversion. */
#undef CPU_CLIPS_NEGATIVE
/* Target processor clips on positive float to int conversion. */
#undef CPU_CLIPS_POSITIVE
/* Target processor is big endian. */
#undef CPU_IS_BIG_ENDIAN
/* Target processor is little endian. */
#undef CPU_IS_LITTLE_ENDIAN
/* Major version of GCC or 3 otherwise. */
#undef GCC_MAJOR_VERSION
/* Define to 1 if you have the `alarm' function. */
#undef HAVE_ALARM
/* Define to 1 if you have the <alsa/asoundlib.h> header file. */
#undef HAVE_ALSA_ASOUNDLIB_H
/* Define to 1 if you have the `calloc' function. */
#undef HAVE_CALLOC
/* Define to 1 if you have the `ceil' function. */
#undef HAVE_CEIL
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Set to 1 if you have libfftw3. */
#undef HAVE_FFTW3
/* Define to 1 if you have the `floor' function. */
#undef HAVE_FLOOR
/* Define to 1 if you have the `fmod' function. */
#undef HAVE_FMOD
/* Define to 1 if you have the `free' function. */
#undef HAVE_FREE
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define if you have C99's lrint function. */
#undef HAVE_LRINT
/* Define if you have C99's lrintf function. */
#undef HAVE_LRINTF
/* Define to 1 if you have the `malloc' function. */
#undef HAVE_MALLOC
/* Define to 1 if you have the `memcpy' function. */
#undef HAVE_MEMCPY
/* Define to 1 if you have the `memmove' function. */
#undef HAVE_MEMMOVE
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define if you have signal SIGALRM. */
#undef HAVE_SIGALRM
/* Define to 1 if you have the `signal' function. */
#undef HAVE_SIGNAL
/* Set to 1 if you have libsndfile. */
#undef HAVE_SNDFILE
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/times.h> header file. */
#undef HAVE_SYS_TIMES_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Set to 1 if compiling for Win32 */
#undef OS_IS_WIN32
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* The size of `double', as computed by sizeof. */
#undef SIZEOF_DOUBLE
/* The size of `float', as computed by sizeof. */
#undef SIZEOF_FLOAT
/* The size of `int', as computed by sizeof. */
#undef SIZEOF_INT
/* The size of `long', as computed by sizeof. */
#undef SIZEOF_LONG
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION

2489
3rdparty/libsamplerate/fastest_coeffs.h vendored Normal file

File diff suppressed because it is too large Load Diff

271
3rdparty/libsamplerate/float_cast.h vendored Normal file
View File

@ -0,0 +1,271 @@
/*
** Copyright (c) 2001-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
/* Version 1.5 */
#ifndef FLOAT_CAST_HEADER
#define FLOAT_CAST_HEADER
/*============================================================================
** On Intel Pentium processors (especially PIII and probably P4), converting
** from float to int is very slow. To meet the C specs, the code produced by
** most C compilers targeting Pentium needs to change the FPU rounding mode
** before the float to int conversion is performed.
**
** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It
** is this flushing of the pipeline which is so slow.
**
** Fortunately the ISO C99 specifications define the functions lrint, lrintf,
** llrint and llrintf which fix this problem as a side effect.
**
** On Unix-like systems, the configure process should have detected the
** presence of these functions. If they weren't found we have to replace them
** here with a standard C cast.
*/
/*
** The C99 prototypes for lrint and lrintf are as follows:
**
** long int lrintf (float x) ;
** long int lrint (double x) ;
*/
#include "config.h"
/*
** The presence of the required functions are detected during the configure
** process and the values HAVE_LRINT and HAVE_LRINTF are set accordingly in
** the config.h file.
*/
#define HAVE_LRINT_REPLACEMENT 0
#if (HAVE_LRINT && HAVE_LRINTF)
/*
** These defines enable functionality introduced with the 1999 ISO C
** standard. They must be defined before the inclusion of math.h to
** engage them. If optimisation is enabled, these functions will be
** inlined. With optimisation switched off, you have to link in the
** maths library using -lm.
*/
#define _ISOC9X_SOURCE 1
#define _ISOC99_SOURCE 1
#define __USE_ISOC9X 1
#define __USE_ISOC99 1
#include <math.h>
#elif (defined (__CYGWIN__))
#include <math.h>
#undef HAVE_LRINT_REPLACEMENT
#define HAVE_LRINT_REPLACEMENT 1
#undef lrint
#undef lrintf
#define lrint double2int
#define lrintf float2int
/*
** The native CYGWIN lrint and lrintf functions are buggy:
** http://sourceware.org/ml/cygwin/2005-06/msg00153.html
** http://sourceware.org/ml/cygwin/2005-09/msg00047.html
** and slow.
** These functions (pulled from the Public Domain MinGW math.h header)
** replace the native versions.
*/
static inline long double2int (double in)
{ long retval ;
__asm__ __volatile__
( "fistpl %0"
: "=m" (retval)
: "t" (in)
: "st"
) ;
return retval ;
} /* double2int */
static inline long float2int (float in)
{ long retval ;
__asm__ __volatile__
( "fistpl %0"
: "=m" (retval)
: "t" (in)
: "st"
) ;
return retval ;
} /* float2int */
#elif (defined (WIN64) || defined(_WIN64))
/* Win64 section should be places before Win32 one, because
** most likely both WIN32 and WIN64 will be defined in 64-bit case.
*/
#include <math.h>
/* Win64 doesn't seem to have these functions, nor inline assembly.
** Therefore implement inline versions of these functions here.
*/
#include <emmintrin.h>
#include <mmintrin.h>
__inline long int
lrint(double flt)
{
return _mm_cvtsd_si32(_mm_load_sd(&flt));
}
__inline long int
lrintf(float flt)
{
return _mm_cvtss_si32(_mm_load_ss(&flt));
}
#elif (defined (WIN32) || defined (_WIN32))
#undef HAVE_LRINT_REPLACEMENT
#define HAVE_LRINT_REPLACEMENT 1
#include <math.h>
/*
** Win32 doesn't seem to have these functions.
** Therefore implement inline versions of these functions here.
*/
__inline long int
lrint (double flt)
{ int intgr ;
_asm
{ fld flt
fistp intgr
} ;
return intgr ;
}
__inline long int
lrintf (float flt)
{ int intgr ;
_asm
{ fld flt
fistp intgr
} ;
return intgr ;
}
#elif (defined (__MWERKS__) && defined (macintosh))
/* This MacOS 9 solution was provided by Stephane Letz */
#undef HAVE_LRINT_REPLACEMENT
#define HAVE_LRINT_REPLACEMENT 1
#include <math.h>
#undef lrint
#undef lrintf
#define lrint double2int
#define lrintf float2int
inline int
float2int (register float in)
{ long res [2] ;
asm
{ fctiw in, in
stfd in, res
}
return res [1] ;
} /* float2int */
inline int
double2int (register double in)
{ long res [2] ;
asm
{ fctiw in, in
stfd in, res
}
return res [1] ;
} /* double2int */
#elif (defined (__MACH__) && defined (__APPLE__))
/* For Apple MacOSX. */
#undef HAVE_LRINT_REPLACEMENT
#define HAVE_LRINT_REPLACEMENT 1
#include <math.h>
#undef lrint
#undef lrintf
#define lrint double2int
#define lrintf float2int
inline static long
float2int (register float in)
{ int res [2] ;
__asm__ __volatile__
( "fctiw %1, %1\n\t"
"stfd %1, %0"
: "=m" (res) /* Output */
: "f" (in) /* Input */
: "memory"
) ;
return res [1] ;
} /* lrintf */
inline static long
double2int (register double in)
{ int res [2] ;
__asm__ __volatile__
( "fctiw %1, %1\n\t"
"stfd %1, %0"
: "=m" (res) /* Output */
: "f" (in) /* Input */
: "memory"
) ;
return res [1] ;
} /* lrint */
#else
#ifndef __sgi
#warning "Don't have the functions lrint() and lrintf()."
#warning "Replacing these functions with a standard C cast."
#endif
#include <math.h>
#define lrint(dbl) ((long) (dbl))
#define lrintf(flt) ((long) (flt))
#endif
#endif /* FLOAT_CAST_HEADER */

340265
3rdparty/libsamplerate/high_qual_coeffs.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsamplerate", "libsamplerate.vcxproj", "{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}"
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
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|Win32.ActiveCfg = Debug|Win32
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|Win32.Build.0 = Debug|Win32
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|x64.ActiveCfg = Debug|x64
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|x64.Build.0 = Debug|x64
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|Win32.ActiveCfg = Release|Win32
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|Win32.Build.0 = Release|Win32
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|x64.ActiveCfg = Release|x64
{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}</ProjectGuid>
<RootNamespace>libsamplerate</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros">
<WindowsSDK80Path Condition="('$(WindowsSDK80Path)'=='')And(Exists('C:\Program Files (x86)\Windows Kits\8.0\'))">C:\Program Files (x86)\Windows Kits\8.0\</WindowsSDK80Path>
<WindowsSDK80Path Condition="('$(WindowsSDK80Path)'=='')And(!Exists('C:\Program Files (x86)\Windows Kits\8.0\'))">$(WindowsSdkDir)</WindowsSDK80Path>
</PropertyGroup>
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</IntDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</IntDir>
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(WindowsSDK80Path)Lib\win8\um\x86;$(DXSDK_DIR)Lib\x86;$(LibraryPath)</LibraryPath>
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(WindowsSDK80Path)Lib\win8\um\x86;$(DXSDK_DIR)Lib\x86;$(LibraryPath)</LibraryPath>
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(WindowsSDK80Path)Lib\win8\um\x64;$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
<LibraryPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(WindowsSDK80Path)Lib\win8\um\x64;$(DXSDK_DIR)Lib\x64;$(LibraryPath)</LibraryPath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(WindowsSDK80Path)Include\um;$(WindowsSDK80Path)Include\shared;$(DXSDK_DIR)Include;$(IncludePath)</IncludePath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(WindowsSDK80Path)Include\um;$(WindowsSDK80Path)Include\shared;$(DXSDK_DIR)Include;$(IncludePath)</IncludePath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(WindowsSDK80Path)Include\um;$(WindowsSDK80Path)Include\shared;$(DXSDK_DIR)Include;$(IncludePath)</IncludePath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(WindowsSDK80Path)Include\um;$(WindowsSDK80Path)Include\shared;$(DXSDK_DIR)Include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSAMPLERATE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSAMPLERATE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSAMPLERATE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalOptions>/d2Zi+ %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSAMPLERATE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalOptions>/d2Zi+ %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="samplerate.c" />
<ClCompile Include="src_linear.c" />
<ClCompile Include="src_sinc.c" />
<ClCompile Include="src_zoh.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h" />
<ClInclude Include="config.h" />
<ClInclude Include="fastest_coeffs.h" />
<ClInclude Include="float_cast.h" />
<ClInclude Include="high_qual_coeffs.h" />
<ClInclude Include="mid_qual_coeffs.h" />
<ClInclude Include="samplerate.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="src_zoh.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="src_linear.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="src_sinc.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="samplerate.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="samplerate.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="config.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="fastest_coeffs.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="float_cast.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="high_qual_coeffs.h">
<Filter>Headers</Filter>
</ClInclude>
<ClInclude Include="mid_qual_coeffs.h">
<Filter>Headers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Headers">
<UniqueIdentifier>{358b26fc-b1ef-4294-a9cc-d6a64be4ba77}</UniqueIdentifier>
</Filter>
<Filter Include="Source">
<UniqueIdentifier>{f23ee9cc-d36b-453e-9cbc-6ac40131f1e6}</UniqueIdentifier>
</Filter>
<Filter Include="Resources">
<UniqueIdentifier>{f3ed8158-880e-4aa4-ac5e-c82086a24375}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

22464
3rdparty/libsamplerate/mid_qual_coeffs.h vendored Normal file

File diff suppressed because it is too large Load Diff

540
3rdparty/libsamplerate/samplerate.c vendored Normal file
View File

@ -0,0 +1,540 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "samplerate.h"
#include "float_cast.h"
#include "common.h"
static int psrc_set_converter (SRC_PRIVATE *psrc, int converter_type) ;
SRC_STATE *
src_new (int converter_type, int channels, int *error)
{ SRC_PRIVATE *psrc ;
if (error)
*error = SRC_ERR_NO_ERROR ;
if (channels < 1)
{ if (error)
*error = SRC_ERR_BAD_CHANNEL_COUNT ;
return NULL ;
} ;
if ((psrc = calloc (1, sizeof (*psrc))) == NULL)
{ if (error)
*error = SRC_ERR_MALLOC_FAILED ;
return NULL ;
} ;
psrc->channels = channels ;
psrc->mode = SRC_MODE_PROCESS ;
if (psrc_set_converter (psrc, converter_type) != SRC_ERR_NO_ERROR)
{ if (error)
*error = SRC_ERR_BAD_CONVERTER ;
free (psrc) ;
psrc = NULL ;
} ;
src_reset ((SRC_STATE*) psrc) ;
return (SRC_STATE*) psrc ;
} /* src_new */
SRC_STATE*
src_callback_new (src_callback_t func, int converter_type, int channels, int *error, void* cb_data)
{ SRC_STATE *src_state ;
if (func == NULL)
{ if (error)
*error = SRC_ERR_BAD_CALLBACK ;
return NULL ;
} ;
if (error != NULL)
*error = 0 ;
if ((src_state = src_new (converter_type, channels, error)) == NULL)
return NULL ;
src_reset (src_state) ;
((SRC_PRIVATE*) src_state)->mode = SRC_MODE_CALLBACK ;
((SRC_PRIVATE*) src_state)->callback_func = func ;
((SRC_PRIVATE*) src_state)->user_callback_data = cb_data ;
return src_state ;
} /* src_callback_new */
SRC_STATE *
src_delete (SRC_STATE *state)
{ SRC_PRIVATE *psrc ;
psrc = (SRC_PRIVATE*) state ;
if (psrc)
{ if (psrc->private_data)
free (psrc->private_data) ;
memset (psrc, 0, sizeof (SRC_PRIVATE)) ;
free (psrc) ;
} ;
return NULL ;
} /* src_state */
int
src_process (SRC_STATE *state, SRC_DATA *data)
{ SRC_PRIVATE *psrc ;
int error ;
psrc = (SRC_PRIVATE*) state ;
if (psrc == NULL)
return SRC_ERR_BAD_STATE ;
if (psrc->vari_process == NULL || psrc->const_process == NULL)
return SRC_ERR_BAD_PROC_PTR ;
if (psrc->mode != SRC_MODE_PROCESS)
return SRC_ERR_BAD_MODE ;
/* Check for valid SRC_DATA first. */
if (data == NULL)
return SRC_ERR_BAD_DATA ;
/* And that data_in and data_out are valid. */
if (data->data_in == NULL || data->data_out == NULL)
return SRC_ERR_BAD_DATA_PTR ;
/* Check src_ratio is in range. */
if (is_bad_src_ratio (data->src_ratio))
return SRC_ERR_BAD_SRC_RATIO ;
if (data->input_frames < 0)
data->input_frames = 0 ;
if (data->output_frames < 0)
data->output_frames = 0 ;
if (data->data_in < data->data_out)
{ if (data->data_in + data->input_frames * psrc->channels > data->data_out)
{ /*-printf ("\n\ndata_in: %p data_out: %p\n",
(void*) (data->data_in + data->input_frames * psrc->channels), (void*) data->data_out) ;-*/
return SRC_ERR_DATA_OVERLAP ;
} ;
}
else if (data->data_out + data->output_frames * psrc->channels > data->data_in)
{ /*-printf ("\n\ndata_in : %p ouput frames: %ld data_out: %p\n", (void*) data->data_in, data->output_frames, (void*) data->data_out) ;
printf ("data_out: %p (%p) data_in: %p\n", (void*) data->data_out,
(void*) (data->data_out + data->input_frames * psrc->channels), (void*) data->data_in) ;-*/
return SRC_ERR_DATA_OVERLAP ;
} ;
/* Set the input and output counts to zero. */
data->input_frames_used = 0 ;
data->output_frames_gen = 0 ;
/* Special case for when last_ratio has not been set. */
if (psrc->last_ratio < (1.0 / SRC_MAX_RATIO))
psrc->last_ratio = data->src_ratio ;
/* Now process. */
if (fabs (psrc->last_ratio - data->src_ratio) < 1e-15)
error = psrc->const_process (psrc, data) ;
else
error = psrc->vari_process (psrc, data) ;
return error ;
} /* src_process */
long
src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data)
{ SRC_PRIVATE *psrc ;
SRC_DATA src_data ;
long output_frames_gen ;
int error = 0 ;
if (state == NULL)
return 0 ;
if (frames <= 0)
return 0 ;
psrc = (SRC_PRIVATE*) state ;
if (psrc->mode != SRC_MODE_CALLBACK)
{ psrc->error = SRC_ERR_BAD_MODE ;
return 0 ;
} ;
if (psrc->callback_func == NULL)
{ psrc->error = SRC_ERR_NULL_CALLBACK ;
return 0 ;
} ;
memset (&src_data, 0, sizeof (src_data)) ;
/* Check src_ratio is in range. */
if (is_bad_src_ratio (src_ratio))
{ psrc->error = SRC_ERR_BAD_SRC_RATIO ;
return 0 ;
} ;
/* Switch modes temporarily. */
src_data.src_ratio = src_ratio ;
src_data.data_out = data ;
src_data.output_frames = frames ;
src_data.data_in = psrc->saved_data ;
src_data.input_frames = psrc->saved_frames ;
output_frames_gen = 0 ;
while (output_frames_gen < frames)
{ /* Use a dummy array for the case where the callback function
** returns without setting the ptr.
*/
float dummy [1] ;
if (src_data.input_frames == 0)
{ float *ptr = dummy ;
src_data.input_frames = psrc->callback_func (psrc->user_callback_data, &ptr) ;
src_data.data_in = ptr ;
if (src_data.input_frames == 0)
src_data.end_of_input = 1 ;
} ;
/*
** Now call process function. However, we need to set the mode
** to SRC_MODE_PROCESS first and when we return set it back to
** SRC_MODE_CALLBACK.
*/
psrc->mode = SRC_MODE_PROCESS ;
error = src_process (state, &src_data) ;
psrc->mode = SRC_MODE_CALLBACK ;
if (error != 0)
break ;
src_data.data_in += src_data.input_frames_used * psrc->channels ;
src_data.input_frames -= src_data.input_frames_used ;
src_data.data_out += src_data.output_frames_gen * psrc->channels ;
src_data.output_frames -= src_data.output_frames_gen ;
output_frames_gen += src_data.output_frames_gen ;
if (src_data.end_of_input == SRC_TRUE && src_data.output_frames_gen == 0)
break ;
} ;
psrc->saved_data = src_data.data_in ;
psrc->saved_frames = src_data.input_frames ;
if (error != 0)
{ psrc->error = error ;
return 0 ;
} ;
return output_frames_gen ;
} /* src_callback_read */
/*==========================================================================
*/
int
src_set_ratio (SRC_STATE *state, double new_ratio)
{ SRC_PRIVATE *psrc ;
psrc = (SRC_PRIVATE*) state ;
if (psrc == NULL)
return SRC_ERR_BAD_STATE ;
if (psrc->vari_process == NULL || psrc->const_process == NULL)
return SRC_ERR_BAD_PROC_PTR ;
if (is_bad_src_ratio (new_ratio))
return SRC_ERR_BAD_SRC_RATIO ;
psrc->last_ratio = new_ratio ;
return SRC_ERR_NO_ERROR ;
} /* src_set_ratio */
int
src_get_channels (SRC_STATE *state)
{ SRC_PRIVATE *psrc ;
psrc = (SRC_PRIVATE*) state ;
if (psrc == NULL)
return SRC_ERR_BAD_STATE ;
if (psrc->vari_process == NULL || psrc->const_process == NULL)
return SRC_ERR_BAD_PROC_PTR ;
return psrc->channels ;
} /* src_get_channels */
int
src_reset (SRC_STATE *state)
{ SRC_PRIVATE *psrc ;
if ((psrc = (SRC_PRIVATE*) state) == NULL)
return SRC_ERR_BAD_STATE ;
if (psrc->reset != NULL)
psrc->reset (psrc) ;
psrc->last_position = 0.0 ;
psrc->last_ratio = 0.0 ;
psrc->saved_data = NULL ;
psrc->saved_frames = 0 ;
psrc->error = SRC_ERR_NO_ERROR ;
return SRC_ERR_NO_ERROR ;
} /* src_reset */
/*==============================================================================
** Control functions.
*/
const char *
src_get_name (int converter_type)
{ const char *desc ;
if ((desc = sinc_get_name (converter_type)) != NULL)
return desc ;
if ((desc = zoh_get_name (converter_type)) != NULL)
return desc ;
if ((desc = linear_get_name (converter_type)) != NULL)
return desc ;
return NULL ;
} /* src_get_name */
const char *
src_get_description (int converter_type)
{ const char *desc ;
if ((desc = sinc_get_description (converter_type)) != NULL)
return desc ;
if ((desc = zoh_get_description (converter_type)) != NULL)
return desc ;
if ((desc = linear_get_description (converter_type)) != NULL)
return desc ;
return NULL ;
} /* src_get_description */
const char *
src_get_version (void)
{ return PACKAGE "-" VERSION " (c) 2002-2008 Erik de Castro Lopo" ;
} /* src_get_version */
int
src_is_valid_ratio (double ratio)
{
if (is_bad_src_ratio (ratio))
return SRC_FALSE ;
return SRC_TRUE ;
} /* src_is_valid_ratio */
/*==============================================================================
** Error reporting functions.
*/
int
src_error (SRC_STATE *state)
{ if (state)
return ((SRC_PRIVATE*) state)->error ;
return SRC_ERR_NO_ERROR ;
} /* src_error */
const char*
src_strerror (int error)
{
switch (error)
{ case SRC_ERR_NO_ERROR :
return "No error." ;
case SRC_ERR_MALLOC_FAILED :
return "Malloc failed." ;
case SRC_ERR_BAD_STATE :
return "SRC_STATE pointer is NULL." ;
case SRC_ERR_BAD_DATA :
return "SRC_DATA pointer is NULL." ;
case SRC_ERR_BAD_DATA_PTR :
return "SRC_DATA->data_out or SRC_DATA->data_in is NULL." ;
case SRC_ERR_NO_PRIVATE :
return "Internal error. No private data." ;
case SRC_ERR_BAD_SRC_RATIO :
return "SRC ratio outside [1/" SRC_MAX_RATIO_STR ", " SRC_MAX_RATIO_STR "] range." ;
case SRC_ERR_BAD_SINC_STATE :
return "src_process() called without reset after end_of_input." ;
case SRC_ERR_BAD_PROC_PTR :
return "Internal error. No process pointer." ;
case SRC_ERR_SHIFT_BITS :
return "Internal error. SHIFT_BITS too large." ;
case SRC_ERR_FILTER_LEN :
return "Internal error. Filter length too large." ;
case SRC_ERR_BAD_CONVERTER :
return "Bad converter number." ;
case SRC_ERR_BAD_CHANNEL_COUNT :
return "Channel count must be >= 1." ;
case SRC_ERR_SINC_BAD_BUFFER_LEN :
return "Internal error. Bad buffer length. Please report this." ;
case SRC_ERR_SIZE_INCOMPATIBILITY :
return "Internal error. Input data / internal buffer size difference. Please report this." ;
case SRC_ERR_BAD_PRIV_PTR :
return "Internal error. Private pointer is NULL. Please report this." ;
case SRC_ERR_DATA_OVERLAP :
return "Input and output data arrays overlap." ;
case SRC_ERR_BAD_CALLBACK :
return "Supplied callback function pointer is NULL." ;
case SRC_ERR_BAD_MODE :
return "Calling mode differs from initialisation mode (ie process v callback)." ;
case SRC_ERR_NULL_CALLBACK :
return "Callback function pointer is NULL in src_callback_read ()." ;
case SRC_ERR_NO_VARIABLE_RATIO :
return "This converter only allows constant conversion ratios." ;
case SRC_ERR_SINC_PREPARE_DATA_BAD_LEN :
return "Internal error : Bad length in prepare_data ()." ;
case SRC_ERR_BAD_INTERNAL_STATE :
return "Error : Someone is trampling on my internal state." ;
case SRC_ERR_MAX_ERROR :
return "Placeholder. No error defined for this error number." ;
default : break ;
}
return NULL ;
} /* src_strerror */
/*==============================================================================
** Simple interface for performing a single conversion from input buffer to
** output buffer at a fixed conversion ratio.
*/
int
src_simple (SRC_DATA *src_data, int converter, int channels)
{ SRC_STATE *src_state ;
int error ;
if ((src_state = src_new (converter, channels, &error)) == NULL)
return error ;
src_data->end_of_input = 1 ; /* Only one buffer worth of input. */
error = src_process (src_state, src_data) ;
src_delete (src_state) ;
return error ;
} /* src_simple */
void
src_short_to_float_array (const short *in, float *out, int len)
{
while (len)
{ len -- ;
out [len] = (float) (in [len] / (1.0 * 0x8000)) ;
} ;
return ;
} /* src_short_to_float_array */
void
src_float_to_short_array (const float *in, short *out, int len)
{ double scaled_value ;
while (len)
{ len -- ;
scaled_value = in [len] * (8.0 * 0x10000000) ;
if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF))
{ out [len] = 32767 ;
continue ;
} ;
if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000))
{ out [len] = -32768 ;
continue ;
} ;
out [len] = (short) (lrint (scaled_value) >> 16) ;
} ;
} /* src_float_to_short_array */
void
src_int_to_float_array (const int *in, float *out, int len)
{
while (len)
{ len -- ;
out [len] = (float) (in [len] / (8.0 * 0x10000000)) ;
} ;
return ;
} /* src_int_to_float_array */
void
src_float_to_int_array (const float *in, int *out, int len)
{ double scaled_value ;
while (len)
{ len -- ;
scaled_value = in [len] * (8.0 * 0x10000000) ;
if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF))
{ out [len] = 0x7fffffff ;
continue ;
} ;
if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000))
{ out [len] = -1 - 0x7fffffff ;
continue ;
} ;
out [len] = lrint (scaled_value) ;
} ;
} /* src_float_to_int_array */
/*==============================================================================
** Private functions.
*/
static int
psrc_set_converter (SRC_PRIVATE *psrc, int converter_type)
{
if (sinc_set_converter (psrc, converter_type) == SRC_ERR_NO_ERROR)
return SRC_ERR_NO_ERROR ;
if (zoh_set_converter (psrc, converter_type) == SRC_ERR_NO_ERROR)
return SRC_ERR_NO_ERROR ;
if (linear_set_converter (psrc, converter_type) == SRC_ERR_NO_ERROR)
return SRC_ERR_NO_ERROR ;
return SRC_ERR_BAD_CONVERTER ;
} /* psrc_set_converter */

183
3rdparty/libsamplerate/samplerate.h vendored Normal file
View File

@ -0,0 +1,183 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
/*
** API documentation is available here:
** http://www.mega-nerd.com/SRC/api.html
*/
#ifndef SAMPLERATE_H
#define SAMPLERATE_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Opaque data type SRC_STATE. */
typedef struct SRC_STATE_tag SRC_STATE ;
/* SRC_DATA is used to pass data to src_simple() and src_process(). */
typedef struct
{ const float *data_in ;
float *data_out ;
long input_frames, output_frames ;
long input_frames_used, output_frames_gen ;
int end_of_input ;
double src_ratio ;
} SRC_DATA ;
/*
** User supplied callback function type for use with src_callback_new()
** and src_callback_read(). First parameter is the same pointer that was
** passed into src_callback_new(). Second parameter is pointer to a
** pointer. The user supplied callback function must modify *data to
** point to the start of the user supplied float array. The user supplied
** function must return the number of frames that **data points to.
*/
typedef long (*src_callback_t) (void *cb_data, float **data) ;
/*
** Standard initialisation function : return an anonymous pointer to the
** internal state of the converter. Choose a converter from the enums below.
** Error returned in *error.
*/
SRC_STATE* src_new (int converter_type, int channels, int *error) ;
/*
** Initilisation for callback based API : return an anonymous pointer to the
** internal state of the converter. Choose a converter from the enums below.
** The cb_data pointer can point to any data or be set to NULL. Whatever the
** value, when processing, user supplied function "func" gets called with
** cb_data as first parameter.
*/
SRC_STATE* src_callback_new (src_callback_t func, int converter_type, int channels,
int *error, void* cb_data) ;
/*
** Cleanup all internal allocations.
** Always returns NULL.
*/
SRC_STATE* src_delete (SRC_STATE *state) ;
/*
** Standard processing function.
** Returns non zero on error.
*/
int src_process (SRC_STATE *state, SRC_DATA *data) ;
/*
** Callback based processing function. Read up to frames worth of data from
** the converter int *data and return frames read or -1 on error.
*/
long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ;
/*
** Simple interface for performing a single conversion from input buffer to
** output buffer at a fixed conversion ratio.
** Simple interface does not require initialisation as it can only operate on
** a single buffer worth of audio.
*/
int src_simple (SRC_DATA *data, int converter_type, int channels) ;
/*
** This library contains a number of different sample rate converters,
** numbered 0 through N.
**
** Return a string giving either a name or a more full description of each
** sample rate converter or NULL if no sample rate converter exists for
** the given value. The converters are sequentially numbered from 0 to N.
*/
const char *src_get_name (int converter_type) ;
const char *src_get_description (int converter_type) ;
const char *src_get_version (void) ;
/*
** Set a new SRC ratio. This allows step responses
** in the conversion ratio.
** Returns non zero on error.
*/
int src_set_ratio (SRC_STATE *state, double new_ratio) ;
/*
** Get the current channel count.
** Returns negative on error, positive channel count otherwise
*/
int src_get_channels (SRC_STATE *state) ;
/*
** Reset the internal SRC state.
** Does not modify the quality settings.
** Does not free any memory allocations.
** Returns non zero on error.
*/
int src_reset (SRC_STATE *state) ;
/*
** Return TRUE if ratio is a valid conversion ratio, FALSE
** otherwise.
*/
int src_is_valid_ratio (double ratio) ;
/*
** Return an error number.
*/
int src_error (SRC_STATE *state) ;
/*
** Convert the error number into a string.
*/
const char* src_strerror (int error) ;
/*
** The following enums can be used to set the interpolator type
** using the function src_set_converter().
*/
enum
{
SRC_SINC_BEST_QUALITY = 0,
SRC_SINC_MEDIUM_QUALITY = 1,
SRC_SINC_FASTEST = 2,
SRC_ZERO_ORDER_HOLD = 3,
SRC_LINEAR = 4,
} ;
/*
** Extra helper functions for converting from short to float and
** back again.
*/
void src_short_to_float_array (const short *in, float *out, int len) ;
void src_float_to_short_array (const float *in, short *out, int len) ;
void src_int_to_float_array (const int *in, float *out, int len) ;
void src_float_to_int_array (const float *in, int *out, int len) ;
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* SAMPLERATE_H */

209
3rdparty/libsamplerate/src_linear.c vendored Normal file
View File

@ -0,0 +1,209 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "float_cast.h"
#include "common.h"
static int linear_vari_process (SRC_PRIVATE *psrc, SRC_DATA *data) ;
static void linear_reset (SRC_PRIVATE *psrc) ;
/*========================================================================================
*/
#define LINEAR_MAGIC_MARKER MAKE_MAGIC ('l', 'i', 'n', 'e', 'a', 'r')
#define SRC_DEBUG 0
typedef struct
{ int linear_magic_marker ;
int channels ;
int reset ;
long in_count, in_used ;
long out_count, out_gen ;
float last_value [1] ;
} LINEAR_DATA ;
/*----------------------------------------------------------------------------------------
*/
static int
linear_vari_process (SRC_PRIVATE *psrc, SRC_DATA *data)
{ LINEAR_DATA *priv ;
double src_ratio, input_index, rem ;
int ch ;
if (data->input_frames <= 0)
return SRC_ERR_NO_ERROR ;
if (psrc->private_data == NULL)
return SRC_ERR_NO_PRIVATE ;
priv = (LINEAR_DATA*) psrc->private_data ;
if (priv->reset)
{ /* If we have just been reset, set the last_value data. */
for (ch = 0 ; ch < priv->channels ; ch++)
priv->last_value [ch] = data->data_in [ch] ;
priv->reset = 0 ;
} ;
priv->in_count = data->input_frames * priv->channels ;
priv->out_count = data->output_frames * priv->channels ;
priv->in_used = priv->out_gen = 0 ;
src_ratio = psrc->last_ratio ;
if (is_bad_src_ratio (src_ratio))
return SRC_ERR_BAD_INTERNAL_STATE ;
input_index = psrc->last_position ;
/* Calculate samples before first sample in input array. */
while (input_index < 1.0 && priv->out_gen < priv->out_count)
{
if (priv->in_used + priv->channels * (1.0 + input_index) >= priv->in_count)
break ;
if (priv->out_count > 0 && fabs (psrc->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = psrc->last_ratio + priv->out_gen * (data->src_ratio - psrc->last_ratio) / priv->out_count ;
for (ch = 0 ; ch < priv->channels ; ch++)
{ data->data_out [priv->out_gen] = (float) (priv->last_value [ch] + input_index *
(data->data_in [ch] - priv->last_value [ch])) ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
} ;
rem = fmod_one (input_index) ;
priv->in_used += priv->channels * lrint (input_index - rem) ;
input_index = rem ;
/* Main processing loop. */
while (priv->out_gen < priv->out_count && priv->in_used + priv->channels * input_index < priv->in_count)
{
if (priv->out_count > 0 && fabs (psrc->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = psrc->last_ratio + priv->out_gen * (data->src_ratio - psrc->last_ratio) / priv->out_count ;
if (SRC_DEBUG && priv->in_used < priv->channels && input_index < 1.0)
{ printf ("Whoops!!!! in_used : %ld channels : %d input_index : %f\n", priv->in_used, priv->channels, input_index) ;
exit (1) ;
} ;
for (ch = 0 ; ch < priv->channels ; ch++)
{ data->data_out [priv->out_gen] = (float) (data->data_in [priv->in_used - priv->channels + ch] + input_index *
(data->data_in [priv->in_used + ch] - data->data_in [priv->in_used - priv->channels + ch])) ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
rem = fmod_one (input_index) ;
priv->in_used += priv->channels * lrint (input_index - rem) ;
input_index = rem ;
} ;
if (priv->in_used > priv->in_count)
{ input_index += (priv->in_used - priv->in_count) / priv->channels ;
priv->in_used = priv->in_count ;
} ;
psrc->last_position = input_index ;
if (priv->in_used > 0)
for (ch = 0 ; ch < priv->channels ; ch++)
priv->last_value [ch] = data->data_in [priv->in_used - priv->channels + ch] ;
/* Save current ratio rather then target ratio. */
psrc->last_ratio = src_ratio ;
data->input_frames_used = priv->in_used / priv->channels ;
data->output_frames_gen = priv->out_gen / priv->channels ;
return SRC_ERR_NO_ERROR ;
} /* linear_vari_process */
/*------------------------------------------------------------------------------
*/
const char*
linear_get_name (int src_enum)
{
if (src_enum == SRC_LINEAR)
return "Linear Interpolator" ;
return NULL ;
} /* linear_get_name */
const char*
linear_get_description (int src_enum)
{
if (src_enum == SRC_LINEAR)
return "Linear interpolator, very fast, poor quality." ;
return NULL ;
} /* linear_get_descrition */
int
linear_set_converter (SRC_PRIVATE *psrc, int src_enum)
{ LINEAR_DATA *priv = NULL ;
if (src_enum != SRC_LINEAR)
return SRC_ERR_BAD_CONVERTER ;
if (psrc->private_data != NULL)
{ free (psrc->private_data) ;
psrc->private_data = NULL ;
} ;
if (psrc->private_data == NULL)
{ priv = calloc (1, sizeof (*priv) + psrc->channels * sizeof (float)) ;
psrc->private_data = priv ;
} ;
if (priv == NULL)
return SRC_ERR_MALLOC_FAILED ;
priv->linear_magic_marker = LINEAR_MAGIC_MARKER ;
priv->channels = psrc->channels ;
psrc->const_process = linear_vari_process ;
psrc->vari_process = linear_vari_process ;
psrc->reset = linear_reset ;
linear_reset (psrc) ;
return SRC_ERR_NO_ERROR ;
} /* linear_set_converter */
/*===================================================================================
*/
static void
linear_reset (SRC_PRIVATE *psrc)
{ LINEAR_DATA *priv = NULL ;
priv = (LINEAR_DATA*) psrc->private_data ;
if (priv == NULL)
return ;
priv->channels = psrc->channels ;
priv->reset = 1 ;
memset (priv->last_value, 0, sizeof (priv->last_value [0]) * priv->channels) ;
return ;
} /* linear_reset */

1193
3rdparty/libsamplerate/src_sinc.c vendored Normal file

File diff suppressed because it is too large Load Diff

200
3rdparty/libsamplerate/src_zoh.c vendored Normal file
View File

@ -0,0 +1,200 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "float_cast.h"
#include "common.h"
static int zoh_vari_process (SRC_PRIVATE *psrc, SRC_DATA *data) ;
static void zoh_reset (SRC_PRIVATE *psrc) ;
/*========================================================================================
*/
#define ZOH_MAGIC_MARKER MAKE_MAGIC ('s', 'r', 'c', 'z', 'o', 'h')
typedef struct
{ int zoh_magic_marker ;
int channels ;
int reset ;
long in_count, in_used ;
long out_count, out_gen ;
float last_value [1] ;
} ZOH_DATA ;
/*----------------------------------------------------------------------------------------
*/
static int
zoh_vari_process (SRC_PRIVATE *psrc, SRC_DATA *data)
{ ZOH_DATA *priv ;
double src_ratio, input_index, rem ;
int ch ;
if (data->input_frames <= 0)
return SRC_ERR_NO_ERROR ;
if (psrc->private_data == NULL)
return SRC_ERR_NO_PRIVATE ;
priv = (ZOH_DATA*) psrc->private_data ;
if (priv->reset)
{ /* If we have just been reset, set the last_value data. */
for (ch = 0 ; ch < priv->channels ; ch++)
priv->last_value [ch] = data->data_in [ch] ;
priv->reset = 0 ;
} ;
priv->in_count = data->input_frames * priv->channels ;
priv->out_count = data->output_frames * priv->channels ;
priv->in_used = priv->out_gen = 0 ;
src_ratio = psrc->last_ratio ;
if (is_bad_src_ratio (src_ratio))
return SRC_ERR_BAD_INTERNAL_STATE ;
input_index = psrc->last_position ;
/* Calculate samples before first sample in input array. */
while (input_index < 1.0 && priv->out_gen < priv->out_count)
{
if (priv->in_used + priv->channels * input_index >= priv->in_count)
break ;
if (priv->out_count > 0 && fabs (psrc->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = psrc->last_ratio + priv->out_gen * (data->src_ratio - psrc->last_ratio) / priv->out_count ;
for (ch = 0 ; ch < priv->channels ; ch++)
{ data->data_out [priv->out_gen] = priv->last_value [ch] ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
} ;
rem = fmod_one (input_index) ;
priv->in_used += priv->channels * lrint (input_index - rem) ;
input_index = rem ;
/* Main processing loop. */
while (priv->out_gen < priv->out_count && priv->in_used + priv->channels * input_index <= priv->in_count)
{
if (priv->out_count > 0 && fabs (psrc->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = psrc->last_ratio + priv->out_gen * (data->src_ratio - psrc->last_ratio) / priv->out_count ;
for (ch = 0 ; ch < priv->channels ; ch++)
{ data->data_out [priv->out_gen] = data->data_in [priv->in_used - priv->channels + ch] ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
rem = fmod_one (input_index) ;
priv->in_used += priv->channels * lrint (input_index - rem) ;
input_index = rem ;
} ;
if (priv->in_used > priv->in_count)
{ input_index += (priv->in_used - priv->in_count) / priv->channels ;
priv->in_used = priv->in_count ;
} ;
psrc->last_position = input_index ;
if (priv->in_used > 0)
for (ch = 0 ; ch < priv->channels ; ch++)
priv->last_value [ch] = data->data_in [priv->in_used - priv->channels + ch] ;
/* Save current ratio rather then target ratio. */
psrc->last_ratio = src_ratio ;
data->input_frames_used = priv->in_used / priv->channels ;
data->output_frames_gen = priv->out_gen / priv->channels ;
return SRC_ERR_NO_ERROR ;
} /* zoh_vari_process */
/*------------------------------------------------------------------------------
*/
const char*
zoh_get_name (int src_enum)
{
if (src_enum == SRC_ZERO_ORDER_HOLD)
return "ZOH Interpolator" ;
return NULL ;
} /* zoh_get_name */
const char*
zoh_get_description (int src_enum)
{
if (src_enum == SRC_ZERO_ORDER_HOLD)
return "Zero order hold interpolator, very fast, poor quality." ;
return NULL ;
} /* zoh_get_descrition */
int
zoh_set_converter (SRC_PRIVATE *psrc, int src_enum)
{ ZOH_DATA *priv = NULL ;
if (src_enum != SRC_ZERO_ORDER_HOLD)
return SRC_ERR_BAD_CONVERTER ;
if (psrc->private_data != NULL)
{ free (psrc->private_data) ;
psrc->private_data = NULL ;
} ;
if (psrc->private_data == NULL)
{ priv = calloc (1, sizeof (*priv) + psrc->channels * sizeof (float)) ;
psrc->private_data = priv ;
} ;
if (priv == NULL)
return SRC_ERR_MALLOC_FAILED ;
priv->zoh_magic_marker = ZOH_MAGIC_MARKER ;
priv->channels = psrc->channels ;
psrc->const_process = zoh_vari_process ;
psrc->vari_process = zoh_vari_process ;
psrc->reset = zoh_reset ;
zoh_reset (psrc) ;
return SRC_ERR_NO_ERROR ;
} /* zoh_set_converter */
/*===================================================================================
*/
static void
zoh_reset (SRC_PRIVATE *psrc)
{ ZOH_DATA *priv ;
priv = (ZOH_DATA*) psrc->private_data ;
if (priv == NULL)
return ;
priv->channels = psrc->channels ;
priv->reset = 1 ;
memset (priv->last_value, 0, sizeof (priv->last_value [0]) * priv->channels) ;
return ;
} /* zoh_reset */

View File

@ -326,6 +326,86 @@ set(pcsx2DEV9Headers
${pcsx2DEV9UIHeaders} ${pcsx2DEV9UIHeaders}
) )
# USB sources
set(pcsx2USBSources
USB/USB.cpp
USB/deviceproxy.cpp
USB/configuration.cpp
USB/osdebugout.cpp
USB/device_init.cpp
USB/qemu-usb/glib.cpp
USB/qemu-usb/vl.cpp
USB/qemu-usb/iov.cpp
USB/qemu-usb/desc.cpp
USB/qemu-usb/core.cpp
USB/qemu-usb/bus.cpp
USB/qemu-usb/usb-ohci.cpp
USB/qemu-usb/hid.cpp
USB/qemu-usb/input-keymap-qcode-to-qnum.cpp
USB/usb-msd/usb-msd.cpp
USB/usb-pad/usb-pad.cpp
USB/usb-pad/usb-pad-ff.cpp
USB/usb-pad/lg/lg_ff.cpp
USB/usb-mic/usb-mic-singstar.cpp
USB/usb-mic/usb-mic-logitech.cpp
USB/usb-mic/usb-headset.cpp
USB/usb-eyetoy/jpgd/jpgd.cpp
USB/usb-eyetoy/jo_mpeg.c
USB/usb-eyetoy/usb-eyetoy-webcam.cpp
USB/usb-hid/usb-hid.cpp
USB/shared/shared.cpp
USB/shared/inifile.cpp
USB/shared/ringbuffer.cpp
)
# USB headers
set(pcsx2USBHeaders
USB/USB.h
USB/PS2Etypes.h
USB/PS2Edefs.h
USB/proxybase.h
USB/deviceproxy.h
USB/configuration.h
USB/platcompat.h
USB/osdebugout.h
USB/helpers.h
USB/readerwriterqueue/readerwriterqueue.h
USB/readerwriterqueue/atomicops.h
USB/qemu-usb/glib.h
USB/qemu-usb/vl.h
USB/qemu-usb/qusb.h
USB/qemu-usb/USBinternal.h
USB/qemu-usb/desc.h
USB/qemu-usb/iov.h
USB/qemu-usb/queue.h
USB/qemu-usb/hid.h
USB/qemu-usb/input-keymap.h
USB/usb-msd/usb-msd.h
USB/usb-pad/usb-pad.h
USB/usb-pad/padproxy.h
USB/usb-pad/lg/lg_ff.h
USB/usb-mic/audio.h
USB/usb-mic/audiodev.h
USB/usb-mic/audiodeviceproxy.h
USB/usb-mic/usb-mic-singstar.h
USB/usb-mic/usb-headset.h
USB/usb-mic/audiodev-noop.h
USB/usb-eyetoy/jpgd/jpgd.h
USB/usb-eyetoy/jo_mpeg.h
USB/usb-eyetoy/videodeviceproxy.h
USB/usb-eyetoy/videodev.h
USB/usb-eyetoy/usb-eyetoy-webcam.h
USB/usb-eyetoy/ov519.h
USB/usb-hid/hidproxy.h
USB/usb-hid/usb-hid.h
USB/usb-hid/noop.h
USB/shared/shared.h
USB/shared/inifile.h
USB/shared/ringbuffer.h
)
# DebugTools sources # DebugTools sources
set(pcsx2DebugToolsSources set(pcsx2DebugToolsSources
@ -761,6 +841,8 @@ set(Common
${pcsx2SPU2Headers} ${pcsx2SPU2Headers}
${pcsx2DEV9Sources} ${pcsx2DEV9Sources}
${pcsx2DEV9Headers} ${pcsx2DEV9Headers}
${pcsx2USBSources}
${pcsx2USBHeaders}
${pcsx2DebugToolsSources} ${pcsx2DebugToolsSources}
${pcsx2GuiSources} ${pcsx2GuiSources}
${pcsx2GuiResources} ${pcsx2GuiResources}

677
pcsx2/USB/USB.cpp Normal file
View File

@ -0,0 +1,677 @@
/* 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 <stdexcept>
#include <cstdlib>
#include <string>
#include <cerrno>
#include <cassert>
#include "version.h" //CMake generated
#include "USB.h"
#include "platcompat.h"
#include "osdebugout.h"
#include "qemu-usb/USBinternal.h"
#include "qemu-usb/desc.h"
#include "shared/shared.h"
#include "deviceproxy.h"
#define PSXCLK 36864000 /* 36.864 Mhz */
static char libraryName[256];
OHCIState *qemu_ohci = NULL;
USBDevice *usb_device[2] = { NULL };
bool configChanged = false;
Config conf;
char USBfreezeID[] = "USBqemuW01";
typedef struct {
char freezeID[11];
s64 cycles;
s64 remaining;
OHCIState t;
struct {
DeviceType index;
u32 size;
USBDevice dev;
} device[2];
struct usb_packet {
USBEndpoint ep; //usb packet endpoint
int dev_index;
int data_size;
} usb_packet;
} USBfreezeData;
u8 *ram = 0;
USBcallback _USBirq;
FILE *usbLog;
int64_t usb_frame_time;
int64_t usb_bit_time;
s64 clocks = 0;
s64 remaining = 0;
#if _WIN32
HWND gsWnd = nullptr;
#else
#include "gtk.h"
#include <gdk/gdkx.h>
#include <X11/X.h>
Display *g_GSdsp;
Window g_GSwin;
#endif
Config::Config(): Log(0)
{
memset(&WheelType, 0, sizeof(WheelType));
}
void USBirq(int cycles)
{
//USB_LOG("USBirq.\n");
_USBirq(cycles);
}
void __Log(const char *fmt, ...) {
va_list list;
if (!conf.Log ||!usbLog) return;
va_start(list, fmt);
vfprintf(usbLog, fmt, list);
va_end(list);
}
//Simpler to reset and reattach after USBclose/USBopen
void Reset()
{
if(qemu_ohci)
ohci_hard_reset(qemu_ohci);
}
void DestroyDevices()
{
for (int i=0; i<2; i++)
{
if(qemu_ohci && qemu_ohci->rhport[i].port.dev) {
qemu_ohci->rhport[i].port.dev->klass.unrealize(qemu_ohci->rhport[i].port.dev);
qemu_ohci->rhport[i].port.dev = nullptr;
}
else if(usb_device[i])
usb_device[i]->klass.unrealize(usb_device[i]);
usb_device[i] = nullptr;
}
}
USBDevice* CreateDevice(DeviceType index, int port)
{
DeviceProxyBase *devProxy;
USBDevice* device = nullptr;
if (index == DEVTYPE_NONE)
return nullptr;
devProxy = RegisterDevice::instance().Device(index);
if (devProxy)
device = devProxy->CreateDevice(port);
else
SysMessage(TEXT("Device %d: Unknown device type"), 1 - port);
if (!device) {
USB_LOG("USBqemu: failed to create device type %d on port %d\n", index, port);
}
return device;
}
//TODO re-do sneaky attach
void USBAttach(int port, USBDevice *dev, bool sneaky = false)
{
if (!qemu_ohci) return;
USBDevice *tmp = qemu_ohci->rhport[port].port.dev;
if (tmp)
{
if (!sneaky)
usb_detach (&qemu_ohci->rhport[port].port);
tmp->klass.unrealize(tmp);
}
qemu_ohci->rhport[port].port.dev = dev;
if (dev)
{
dev->attached = true;
usb_attach (&qemu_ohci->rhport[port].port); //.ops->attach(&(qemu_ohci->rhport[port].port));
}
}
USBDevice* CreateDevice(const std::string& name, int port)
{
DeviceProxyBase *devProxy;
USBDevice* device = nullptr;
if (!name.empty())
{
devProxy = RegisterDevice::instance().Device(name);
if (devProxy)
device = devProxy->CreateDevice(port);
else
SysMessage(TEXT("Port %d: Unknown device type"), port);
}
if (!device) {
USB_LOG("USBqemu: failed to create device '%s' on port %d\n", name.c_str(), port);
}
return device;
}
void CreateDevices()
{
if(!qemu_ohci) return; //No USBinit yet ie. called from config. dialog
DestroyDevices();
for (int i=0; i<2; i++)
{
usb_device[i] = CreateDevice(conf.Port[i], i);
USBAttach(i, usb_device[i]);
}
}
EXPORT_C_(s32) USBinit() {
OSDebugOut(TEXT("USBinit\n"));
RegisterDevice::Register();
LoadConfig();
if (conf.Log && !usbLog)
{
usbLog =
#if _UNICODE
_wfopen(LogDir.c_str(), L"wb");// L"wb,ccs=UNICODE");
#else
fopen(LogDir.c_str(), "w");
#endif
//if(usbLog) setvbuf(usbLog, NULL, _IONBF, 0);
USB_LOG("usbqemu wheel mod plugin version %d.%d.%d\n", VER_REV, VER_BLD, VER_FIX);
USB_LOG("USBinit\n");
}
qemu_ohci = ohci_create(0x1f801600,2);
if(!qemu_ohci) return 1;
clocks = 0;
remaining = 0;
return 0;
}
EXPORT_C_(void) USBshutdown() {
OSDebugOut(TEXT("USBshutdown\n"));
DestroyDevices();
RegisterDevice::instance().Unregister();
free(qemu_ohci);
ram = 0;
//#ifdef _DEBUG
if (conf.Log && usbLog) {
fclose(usbLog);
usbLog = nullptr;
}
//#endif
}
EXPORT_C_(s32) USBopen(void *pDsp) {
if (conf.Log && !usbLog)
{
usbLog = fopen("logs/usbLog.txt", "a");
//if(usbLog) setvbuf(usbLog, NULL, _IONBF, 0);
USB_LOG("usbqemu wheel mod plugin version %d.%d.%d\n", VER_REV, VER_BLD, VER_FIX);
}
USB_LOG("USBopen\n");
OSDebugOut(TEXT("USBopen\n"));
#if _WIN32
HWND hWnd=(HWND)pDsp;
//HWND hWnd=(HWND)((uptr*)pDsp)[0];
if (!IsWindow (hWnd))
hWnd = *(HWND*)hWnd;
if (!IsWindow (hWnd))
hWnd = NULL;
else
{
while (GetWindowLong (hWnd, GWL_STYLE) & WS_CHILD)
hWnd = GetParent (hWnd);
}
gsWnd = hWnd;
pDsp = gsWnd;
#else
g_GSdsp = (Display *)((uptr*)pDsp)[0];
g_GSwin = (Window)((uptr*)pDsp)[1];
OSDebugOut("X11 display %p Xwindow %lu\n", g_GSdsp, g_GSwin);
#endif
try {
shared::Initialize(pDsp);
} catch (std::runtime_error &e) {
SysMessage(TEXT("%" SFMTs "\n"), e.what());
}
if (configChanged || (!usb_device[0] && !usb_device[1]))
{
configChanged = false;
CreateDevices(); //TODO Pass pDsp to init?
}
//TODO Pass pDsp to open probably so dinput can bind to this HWND
if(usb_device[0] && usb_device[0]->klass.open)
usb_device[0]->klass.open(usb_device[0]/*, pDsp*/);
if(usb_device[1] && usb_device[1]->klass.open)
usb_device[1]->klass.open(usb_device[1]/*, pDsp*/);
return 0;
}
EXPORT_C_(void) USBclose() {
OSDebugOut(TEXT("USBclose\n"));
if(usb_device[0] && usb_device[0]->klass.close)
usb_device[0]->klass.close(usb_device[0]);
if(usb_device[1] && usb_device[1]->klass.close)
usb_device[1]->klass.close(usb_device[1]);
shared::Uninitialize();
}
EXPORT_C_(u8) USBread8(u32 addr) {
USB_LOG("* Invalid 8bit read at address %lx\n", addr);
return 0;
}
EXPORT_C_(u16) USBread16(u32 addr) {
USB_LOG("* Invalid 16bit read at address %lx\n", addr);
return 0;
}
EXPORT_C_(u32) 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;
}
EXPORT_C_(void) USBwrite8(u32 addr, u8 value) {
USB_LOG("* Invalid 8bit write at address %lx value %x\n", addr, value);
}
EXPORT_C_(void) USBwrite16(u32 addr, u16 value) {
USB_LOG("* Invalid 16bit write at address %lx value %x\n", addr, value);
}
EXPORT_C_(void) 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);
}
EXPORT_C_(void) USBirqCallback(USBcallback callback) {
_USBirq = callback;
}
extern u32 bits;
EXPORT_C_(int) _USBirqHandler(void)
{
//fprintf(stderr," * USB: IRQ Acknowledged.\n");
//qemu_ohci->intr_status&=~bits;
return 1;
}
EXPORT_C_(USBhandler) USBirqHandler(void) {
return (USBhandler)_USBirqHandler;
}
EXPORT_C_(void) USBsetRAM(void *mem) {
ram = (u8*)mem;
Reset();
}
EXPORT_C_(s32) USBfreeze(int mode, freezeData *data) {
USBfreezeData usbd = { 0 };
//TODO FREEZE_SIZE mismatch causes loading to fail in PCSX2 beforehand
if (mode == FREEZE_LOAD)
{
if(data->size < sizeof(USBfreezeData))
{
SysMessage(TEXT("ERROR: Unable to load freeze data! Got %d bytes, expected >= %d.\n"), data->size, sizeof(USBfreezeData));
return -1;
}
usbd = *(USBfreezeData*)data->data;
usbd.freezeID[10] = 0;
if( strcmp(usbd.freezeID, USBfreezeID) != 0)
{
SysMessage(TEXT("ERROR: Unable to load freeze data! Found ID '%") TEXT(SFMTs) TEXT("', expected ID '%") TEXT(SFMTs) TEXT("'.\n"), usbd.freezeID, USBfreezeID);
return -1;
}
//TODO Subsequent save state loadings make USB "stall" for n seconds since previous load
//clocks = usbd.cycles;
//remaining = usbd.remaining;
for(int i=0; i< qemu_ohci->num_ports; i++)
{
usbd.t.rhport[i].port.opaque = qemu_ohci;
usbd.t.rhport[i].port.ops = qemu_ohci->rhport[i].port.ops;
usbd.t.rhport[i].port.dev = qemu_ohci->rhport[i].port.dev;
}
*qemu_ohci = usbd.t;
s8 *ptr = data->data + sizeof(USBfreezeData);
// Load the state of the attached devices
if (data->size != sizeof(USBfreezeData) + usbd.device[0].size + usbd.device[1].size + 8192)
return -1;
RegisterDevice& regInst = RegisterDevice::instance();
for (int i=0; i<2; i++)
{
auto index = regInst.Index(conf.Port[i]);
auto proxy = regInst.Device(index);
//TODO FREEZE_SIZE mismatch causes loading to fail in PCSX2 beforehand
// but just in case, recreate the same device type as was saved
if (usbd.device[i].index != index)
{
index = usbd.device[i].index;
USBDevice *dev = qemu_ohci->rhport[i].port.dev;
qemu_ohci->rhport[i].port.dev = nullptr;
if (dev)
{
assert(usb_device[i] == dev);
dev->klass.unrealize(dev);
}
proxy = regInst.Device(index);
usb_device[i] = CreateDevice(index, i);
USBAttach(i, usb_device[i], index != DEVTYPE_MSD);
}
if (proxy && usb_device[i]) /* usb device creation may have failed for some reason */
{
if (proxy->Freeze(FREEZE_SIZE, usb_device[i], nullptr) != usbd.device[i].size)
{
SysMessage(TEXT("Port %d: device's freeze size doesn't match.\n"), 1+(1-i));
return -1;
}
const USBDevice& tmp = usbd.device[i].dev;
usb_device[i]->addr = tmp.addr;
usb_device[i]->attached = tmp.attached;
usb_device[i]->auto_attach = tmp.auto_attach;
usb_device[i]->configuration = tmp.configuration;
usb_device[i]->ninterfaces = tmp.ninterfaces;
usb_device[i]->flags = tmp.flags;
usb_device[i]->state = tmp.state;
usb_device[i]->remote_wakeup = tmp.remote_wakeup;
usb_device[i]->setup_state = tmp.setup_state;
usb_device[i]->setup_len = tmp.setup_len;
usb_device[i]->setup_index = tmp.setup_index;
memcpy(usb_device[i]->data_buf, tmp.data_buf, sizeof(tmp.data_buf));
memcpy(usb_device[i]->setup_buf, tmp.setup_buf, sizeof(tmp.setup_buf));
usb_desc_set_config(usb_device[i], tmp.configuration);
for (int k = 0; k < 16; k++) {
usb_device[i]->altsetting[k] = tmp.altsetting[k];
usb_desc_set_interface(usb_device[i], k, tmp.altsetting[k]);
}
proxy->Freeze(FREEZE_LOAD, usb_device[i], ptr);
//TODO reset port if save state's and configured wheel types are different
usb_detach (&qemu_ohci->rhport[i].port);
usb_attach (&qemu_ohci->rhport[i].port);
}
else if (!proxy && index != DEVTYPE_NONE)
{
SysMessage(TEXT("Port %d: unknown device.\nPlugin is probably too old for this save.\n"), 1+(1-i));
return -1;
}
ptr += usbd.device[i].size;
}
int dev_index = usbd.usb_packet.dev_index;
// restore USBPacket for OHCIState
//if (qemu_ohci->usb_packet.iov.iov)
usb_packet_cleanup(&qemu_ohci->usb_packet);
usb_packet_init(&qemu_ohci->usb_packet);
if (usb_device[dev_index])
{
USBPacket *p = &qemu_ohci->usb_packet;
p->actual_length = usbd.usb_packet.data_size;
QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
iov_from_buf(iov->iov, iov->niov, 0, ptr, p->actual_length);
if (usbd.usb_packet.ep.pid == USB_TOKEN_SETUP)
{
if (usb_device[dev_index]->ep_ctl.ifnum == usbd.usb_packet.ep.ifnum)
qemu_ohci->usb_packet.ep = &usb_device[dev_index]->ep_ctl;
}
else
{
USBEndpoint *eps = nullptr;
if (usbd.usb_packet.ep.pid == USB_TOKEN_IN)
eps = usb_device[dev_index]->ep_in;
else //if (usbd.ep.pid == USB_TOKEN_OUT)
eps = usb_device[dev_index]->ep_out;
for (int k = 0; k < USB_MAX_ENDPOINTS; k++) {
if (usbd.usb_packet.ep.type == eps[k].type
&& usbd.usb_packet.ep.nr == eps[k].nr
&& usbd.usb_packet.ep.ifnum == eps[k].ifnum
&& usbd.usb_packet.ep.pid == eps[k].pid)
{
qemu_ohci->usb_packet.ep = &eps[k];
break;
}
}
}
}
else
{
SysMessage(TEXT("USB packet has invalid device index? %d\n"), dev_index); // or just a bug
return -1;
}
}
//TODO straight copying of structs can break cross-platform/cross-compiler save states 'cause padding 'n' stuff
else if (mode == FREEZE_SAVE)
{
memset(data->data, 0, data->size);//maybe it already is...
RegisterDevice& regInst = RegisterDevice::instance();
usbd.usb_packet.dev_index = -1;
for (int i=0; i<2; i++)
{
//TODO check that current created usb device and conf.Port[n] are the same
auto index = regInst.Index(conf.Port[i]);
auto proxy = regInst.Device(index);
usbd.device[i].index = index;
if (proxy)
usbd.device[i].size = proxy->Freeze(FREEZE_SIZE, usb_device[i], nullptr);
else
usbd.device[i].size = 0;
if (qemu_ohci->usb_packet.ep && qemu_ohci->usb_packet.ep->dev == usb_device[i])
usbd.usb_packet.dev_index = i;
}
strncpy(usbd.freezeID, USBfreezeID, strlen(USBfreezeID));
usbd.t = *qemu_ohci;
usbd.usb_packet.ep = qemu_ohci->usb_packet.ep ? *qemu_ohci->usb_packet.ep : USBEndpoint{0};
usbd.t.usb_packet.iov = { };
usbd.t.usb_packet.ep = nullptr;
for(int i=0; i< qemu_ohci->num_ports; i++)
{
usbd.t.rhport[i].port.opaque = nullptr;
usbd.t.rhport[i].port.ops = nullptr;
usbd.t.rhport[i].port.dev = nullptr;
}
usbd.cycles = clocks;
usbd.remaining = remaining;
s8 *ptr = data->data + sizeof(USBfreezeData);
// Save the state of the attached devices
for (int i=0; i<2; i++)
{
auto proxy = regInst.Device(conf.Port[i]);
if (proxy && usbd.device[i].size)
{
proxy->Freeze(FREEZE_SAVE, usb_device[i], ptr);
if (usb_device[i])
usbd.device[i].dev = *usb_device[i];
memset (&usbd.device[i].dev.klass, 0, sizeof(USBDeviceClass));
}
ptr += usbd.device[i].size;
}
USBPacket *p = &qemu_ohci->usb_packet;
usbd.usb_packet.data_size = p->actual_length;
QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
iov_to_buf(iov->iov, iov->niov, 0, ptr, p->actual_length);
*(USBfreezeData*)data->data = usbd;
}
else if (mode == FREEZE_SIZE)
{
RegisterDevice& regInst = RegisterDevice::instance();
data->size = sizeof(USBfreezeData);
for (int i=0; i<2; i++)
{
//TODO check that current created usb device and conf.Port[n] are the same
auto proxy = regInst.Device(conf.Port[i]);
if (proxy)
data->size += proxy->Freeze(FREEZE_SIZE, usb_device[i], nullptr);
}
// PCSX2 queries size before load too, so can't use actual packet length which varies :(
data->size += 8192;// qemu_ohci->usb_packet.actual_length;
if (qemu_ohci->usb_packet.actual_length > 8192) {
fprintf(stderr, "Saving failed! USB packet is larger than 8K, try again later.\n");
return -1;
}
}
return 0;
}
EXPORT_C_(void) USBasync(u32 cycles)
{
remaining += cycles;
clocks += remaining;
if(qemu_ohci->eof_timer>0)
{
while(remaining>=qemu_ohci->eof_timer)
{
remaining-=qemu_ohci->eof_timer;
qemu_ohci->eof_timer=0;
ohci_frame_boundary(qemu_ohci);
/*
* Break out of the loop if bus was stopped.
* If ohci_frame_boundary hits an UE, but doesn't stop processing,
* it seems to cause a hang inside the game instead.
*/
if (!qemu_ohci->eof_timer)
break;
}
if((remaining>0)&&(qemu_ohci->eof_timer>0))
{
s64 m = qemu_ohci->eof_timer;
if(remaining < m)
m = remaining;
qemu_ohci->eof_timer -= m;
remaining -= m;
}
}
//if(qemu_ohci->eof_timer <= 0)
//{
//ohci_frame_boundary(qemu_ohci);
//}
}
EXPORT_C_(s32) USBtest() {
return 0;
}
int cpu_physical_memory_rw(u32 addr, u8 *buf, size_t len, int is_write)
{
//OSDebugOut(TEXT("%s addr %08X, len %d\n"), is_write ? TEXT("write") : TEXT("read "), addr, len);
// invalid address, reset and try again
if (addr+len >= 0x200000)
{
OSDebugOut(TEXT("invalid address, soft resetting ohci.\n"));
if (qemu_ohci)
ohci_soft_reset(qemu_ohci);
return 1;
}
if(is_write)
memcpy(&(ram[addr]),buf,len);
else
memcpy(buf,&(ram[addr]),len);
return 0;
}
int get_ticks_per_second()
{
return PSXCLK;
}
s64 get_clock()
{
return clocks;
}

58
pcsx2/USB/USB.h Normal file
View File

@ -0,0 +1,58 @@
/* 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 __PS2USB_H__
#define __PS2USB_H__
#include <cstdio>
#include <cstring>
#include <string>
#include <limits.h>
#include "platcompat.h"
#include "osdebugout.h"
// ---------------------------------------------------------------------
#define USBdefs
extern u8 *ram;
// ---------------------------------------------------------------------
extern USBcallback _USBirq;
void USBirq(int);
void DestroyDevices();
void CreateDevices();
extern FILE *usbLog;
s64 get_clock();
/* usb-pad-raw.cpp */
#if _WIN32
extern HWND gsWnd;
# if defined(BUILD_RAW)
extern HWND msgWindow;
int InitWindow(HWND);
void UninitWindow();
# endif
#endif
#endif

249
pcsx2/USB/Win32/Config.cpp Normal file
View File

@ -0,0 +1,249 @@
#include "../USB.h"
#include "resource.h"
#include "Config.h"
#include "../deviceproxy.h"
#include "../usb-pad/padproxy.h"
#include "../usb-mic/audiodeviceproxy.h"
#include "../configuration.h"
HINSTANCE hInst;
extern bool configChanged;
void SysMessageA(const char *fmt, ...) {
va_list list;
char tmp[512];
va_start(list, fmt);
vsprintf_s(tmp, 512, fmt, list);
va_end(list);
MessageBoxA(0, tmp, "Qemu USB Msg", 0);
}
void SysMessageW(const wchar_t *fmt, ...) {
va_list list;
wchar_t tmp[512];
va_start(list, fmt);
vswprintf_s(tmp, 512, fmt, list);
va_end(list);
MessageBoxW(0, tmp, L"Qemu USB Msg", 0);
}
void SelChangedAPI(HWND hW, int port)
{
int sel = SendDlgItemMessage(hW, port ? IDC_COMBO_API1 : IDC_COMBO_API2, CB_GETCURSEL, 0, 0);
int devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1 : IDC_COMBO2, CB_GETCURSEL, 0, 0);
if (devtype == 0)
return;
devtype--;
auto& rd = RegisterDevice::instance();
auto devName = rd.Name(devtype);
auto apis = rd.Device(devtype)->ListAPIs();
auto it = apis.begin();
std::advance(it, sel);
changedAPIs[std::make_pair(port, devName)] = *it;
}
void PopulateAPIs(HWND hW, int port)
{
OSDebugOut(TEXT("Populate api %d\n"), port);
SendDlgItemMessage(hW, port ? IDC_COMBO_API1 : IDC_COMBO_API2, CB_RESETCONTENT, 0, 0);
int devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1 : IDC_COMBO2, CB_GETCURSEL, 0, 0);
if (devtype == 0)
return;
devtype--;
auto& rd = RegisterDevice::instance();
auto dev = rd.Device(devtype);
auto devName = rd.Name(devtype);
auto apis = dev->ListAPIs();
std::string selApi = GetSelectedAPI(std::make_pair(port, devName));
std::string var;
if (LoadSetting(nullptr, port, rd.Name(devtype), N_DEVICE_API, var))
OSDebugOut(L"Current API: %S\n", var.c_str());
else
{
if (apis.begin() != apis.end())
{
selApi = *apis.begin();
changedAPIs[std::make_pair(port, devName)] = selApi;
}
}
int i = 0, sel = 0;
for (auto& api : apis)
{
auto name = dev->LongAPIName(api);
if (!name)
continue;
SendDlgItemMessageW(hW, port ? IDC_COMBO_API1 : IDC_COMBO_API2, CB_ADDSTRING, 0, (LPARAM)name);
if (api == var)
sel = i;
i++;
}
SendDlgItemMessage(hW, port ? IDC_COMBO_API1 : IDC_COMBO_API2, CB_SETCURSEL, sel, 0);
}
BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) {
int port;
switch(uMsg) {
case WM_INITDIALOG:
SendDlgItemMessageA(hW, IDC_BUILD_DATE, WM_SETTEXT, 0, (LPARAM)__DATE__ " " __TIME__);
LoadConfig();
CheckDlgButton(hW, IDC_LOGGING, conf.Log);
//Selected emulated devices.
SendDlgItemMessageA(hW, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)"None");
SendDlgItemMessageA(hW, IDC_COMBO2, CB_ADDSTRING, 0, (LPARAM)"None");
{
auto& rd = RegisterDevice::instance();
int i = 0, p1 = 0, p2 = 0;
for (auto& name : rd.Names())
{
i++; //jump over "None"
auto dev = rd.Device(name);
SendDlgItemMessageW(hW, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)dev->Name());
SendDlgItemMessageW(hW, IDC_COMBO2, CB_ADDSTRING, 0, (LPARAM)dev->Name());
//Port 1 aka device/player 1
if (conf.Port[1] == name)
p1 = i;
//Port 0 aka device/player 2
if (conf.Port[0] == name)
p2 = i;
}
SendDlgItemMessage(hW, IDC_COMBO1, CB_SETCURSEL, p1, 0);
SendDlgItemMessage(hW, IDC_COMBO2, CB_SETCURSEL, p2, 0);
PopulateAPIs(hW, 0);
PopulateAPIs(hW, 1);
}
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE1, CB_ADDSTRING, 0, (LPARAM)"Driving Force");
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE1, CB_ADDSTRING, 0, (LPARAM)"Driving Force Pro");
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE1, CB_ADDSTRING, 0, (LPARAM)"Driving Force Pro (rev11.02)");
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE1, CB_ADDSTRING, 0, (LPARAM)"GT Force");
SendDlgItemMessage(hW, IDC_COMBO_WHEEL_TYPE1, CB_SETCURSEL, conf.WheelType[PLAYER_ONE_PORT], 0);
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE2, CB_ADDSTRING, 0, (LPARAM)"Driving Force");
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE2, CB_ADDSTRING, 0, (LPARAM)"Driving Force Pro");
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE2, CB_ADDSTRING, 0, (LPARAM)"Driving Force Pro (rev11.02)");
SendDlgItemMessageA(hW, IDC_COMBO_WHEEL_TYPE2, CB_ADDSTRING, 0, (LPARAM)"GT Force");
SendDlgItemMessage(hW, IDC_COMBO_WHEEL_TYPE2, CB_SETCURSEL, conf.WheelType[PLAYER_TWO_PORT], 0);
return TRUE;
break;
case WM_COMMAND:
switch (HIWORD(wParam))
{
case CBN_SELCHANGE:
switch (LOWORD(wParam)) {
case IDC_COMBO_API1:
case IDC_COMBO_API2:
port = (LOWORD(wParam) == IDC_COMBO_API1) ? 1 : 0;
SelChangedAPI(hW, port);
break;
case IDC_COMBO1:
case IDC_COMBO2:
port = (LOWORD(wParam) == IDC_COMBO1) ? 1 : 0;
PopulateAPIs(hW, port);
break;
}
break;
case BN_CLICKED:
switch(LOWORD(wParam)) {
case IDC_CONFIGURE1:
case IDC_CONFIGURE2:
{
LRESULT devtype, apitype;
port = (LOWORD(wParam) == IDC_CONFIGURE1) ? 1 : 0;
devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1 : IDC_COMBO2, CB_GETCURSEL, 0, 0);
apitype = SendDlgItemMessage(hW, port ? IDC_COMBO_API1 : IDC_COMBO_API2, CB_GETCURSEL, 0, 0);
if (devtype > 0)
{
devtype--;
auto device = RegisterDevice::instance().Device(devtype);
if (device)
{
auto list = device->ListAPIs();
auto it = list.begin();
std::advance(it, apitype);
if (it == list.end())
break;
std::string api = *it;
Win32Handles handles(hInst, hW);
if (device->Configure(port, api, &handles) == RESULT_FAILED)
SysMessage(TEXT("Some settings may not have been saved!\n"));
}
}
}
break;
case IDCANCEL:
EndDialog(hW, TRUE);
return TRUE;
case IDOK:
conf.Log = IsDlgButtonChecked(hW, IDC_LOGGING);
{
auto& regInst = RegisterDevice::instance();
int i;
//device type
i = SendDlgItemMessage(hW, IDC_COMBO1, CB_GETCURSEL, 0, 0);
conf.Port[1] = regInst.Name(i - 1);
i = SendDlgItemMessage(hW, IDC_COMBO2, CB_GETCURSEL, 0, 0);
conf.Port[0] = regInst.Name(i - 1);
}
//wheel type
conf.WheelType[PLAYER_ONE_PORT] = SendDlgItemMessage(hW, IDC_COMBO_WHEEL_TYPE1, CB_GETCURSEL, 0, 0);
conf.WheelType[PLAYER_TWO_PORT] = SendDlgItemMessage(hW, IDC_COMBO_WHEEL_TYPE2, CB_GETCURSEL, 0, 0);
SaveConfig();
CreateDevices();
EndDialog(hW, RESULT_OK);
configChanged = true;
return TRUE;
}
}
}
return FALSE;
}
EXPORT_C_(BOOL) 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;
}
EXPORT_C_(void) USBconfigure() {
RegisterDevice::Register();
DialogBox(hInst,
MAKEINTRESOURCE(IDD_CONFIG),
GetActiveWindow(),
(DLGPROC)ConfigureDlgProc);
}
EXPORT_C_(void) USBabout() {
DialogBox(hInst,
MAKEINTRESOURCE(IDD_ABOUT),
GetActiveWindow(),
(DLGPROC)AboutDlgProc);
}
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD dwReason,
LPVOID lpReserved) {
hInst = (HINSTANCE)hModule;
return TRUE;
}

29
pcsx2/USB/Win32/Config.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef WIN32_H
#define WIN32_H
#include <commctrl.h>
typedef struct Win32Handles
{
HINSTANCE hInst;
HWND hWnd;
Win32Handles(HINSTANCE i, HWND w):
hInst(i),
hWnd(w)
{
}
} Win32Handles;
#define CHECKED_SET_MAX_INT(var, hDlg, nIDDlgItem, bSigned, min, max)\
do {\
/*CheckControlTextIsNumber(GetDlgItem(hDlg, nIDDlgItem), bSigned, 0);*/\
var = GetDlgItemInt(hDlg, nIDDlgItem, NULL, bSigned);\
if (var < min)\
var = min;\
else if (var > max)\
{\
var = max;\
SetDlgItemInt(hDlg, nIDDlgItem, var, bSigned);\
SendMessage(GetDlgItem(hDlg, nIDDlgItem), EM_SETSEL, -2, -2);\
}\
} while (0)
#endif

208
pcsx2/USB/Win32/USBlinuz.rc Normal file
View File

@ -0,0 +1,208 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winresrc.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_DLGMSD DIALOGEX 0, 0, 316, 74
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "USB mass storage device"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,204,54,50,14
PUSHBUTTON "Cancel",IDCANCEL,258,54,50,14
GROUPBOX "Image file path",IDC_STATIC,6,6,300,36
EDITTEXT IDC_EDIT1,12,18,234,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES
PUSHBUTTON "Browse",IDC_BUTTON1,252,18,50,14
END
IDD_CONFIG DIALOGEX 0, 0, 257, 205
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Qemu USB Configuration"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,194,184,50,14
CONTROL "Enable Logging (for developer use only)",IDC_LOGGING,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,102,144,12
COMBOBOX IDC_COMBO1,48,17,198,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Port 1:",IDC_STATIC,19,19,23,8
COMBOBOX IDC_COMBO2,48,35,198,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Port 2:",IDC_STATIC,19,37,23,8
LTEXT "Static",IDC_BUILD_DATE,6,190,114,8
DEFPUSHBUTTON "Configure",IDC_CONFIGURE1,198,64,50,14
DEFPUSHBUTTON "Configure",IDC_CONFIGURE2,198,82,50,14
DEFPUSHBUTTON "OK",IDOK,138,184,50,14
COMBOBOX IDC_COMBO_WHEEL_TYPE1,42,144,192,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Port 1:",IDC_STATIC,12,144,23,8
GROUPBOX "Emulated wheel",IDC_STATIC,6,132,240,48
COMBOBOX IDC_COMBO_WHEEL_TYPE2,42,162,192,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Port 2:",IDC_STATIC,12,162,23,8
GROUPBOX "Device type",IDC_STATIC,6,6,246,48
COMBOBOX IDC_COMBO_API1,48,65,144,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBO_API2,48,83,144,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
GROUPBOX "Device API",IDC_STATIC,6,54,246,48
LTEXT "Port 1:",IDC_STATIC,18,66,23,8
LTEXT "Port 2:",IDC_STATIC,18,84,23,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,43
LTEXT "Author: gigaherz <gigaherz@pcsx2.net>",IDC_STATIC,20,20,141,10
LTEXT "Parts of this code were originally from the QEMU project.",IDC_STATIC,14,44,149,16
END
IDD_DLGWASAPI DIALOGEX 0, 0, 287, 230
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "WASAPI settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,174,210,50,14
PUSHBUTTON "Cancel",IDCANCEL,228,210,50,14
GROUPBOX "Audio Input",IDC_STATIC,6,6,270,54
LTEXT "Player 1:",IDC_STATIC,12,18,30,8
LTEXT "Player 2:",IDC_STATIC,12,36,30,8
COMBOBOX IDC_COMBO1,48,18,216,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBO2,48,36,216,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "1 ms",IDC_STATIC,18,138,16,8
LTEXT "1000 ms",IDC_STATIC,240,138,28,8
GROUPBOX "Input Buffering",IDC_STATIC,6,108,270,46
CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,18,121,252,15
EDITTEXT IDC_BUFFER1,122,138,40,12,ES_AUTOHSCROLL | ES_NUMBER
GROUPBOX "Audio Output",IDC_STATIC,6,66,270,36
COMBOBOX IDC_COMBO3,48,78,216,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "1 ms",IDC_STATIC,18,186,16,8
LTEXT "1000 ms",IDC_STATIC,240,186,28,8
GROUPBOX "Output Buffering",IDC_STATIC,6,156,270,46
CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,18,169,252,15
EDITTEXT IDC_BUFFER2,122,186,40,12,ES_AUTOHSCROLL | ES_NUMBER
END
IDD_DLG_EYETOY DIALOGEX 0, 0, 309, 49
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "EyeToy settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Device:",IDC_STATIC,7,8,27,8
COMBOBOX IDC_COMBO1,40,7,262,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
DEFPUSHBUTTON "OK",IDOK,198,28,50,14
PUSHBUTTON "Cancel",IDCANCEL,252,28,50,14
END
/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//
IDD_DLGMIC AFX_DIALOG_LAYOUT
BEGIN
0
END
IDD_CONFIG AFX_DIALOG_LAYOUT
BEGIN
0
END
IDD_DLGWASAPI AFX_DIALOG_LAYOUT
BEGIN
0
END
IDD_DLG_EYETOY AFX_DIALOG_LAYOUT
BEGIN
0
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DLGWASAPI, DIALOG
BEGIN
END
END
#endif // APSTUDIO_INVOKED
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Spanish (Argentina) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESS)
LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""WinResrc.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // Spanish (Argentina) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

4
pcsx2/USB/Win32/guid.cpp Normal file
View File

@ -0,0 +1,4 @@
#include <windows.h>
#include <initguid.h>
#include <propsys.h>
#include <functiondiscoverykeys_devpkey.h>

View File

@ -0,0 +1,44 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by USBlinuz.rc
//
#define IDC_STATIC -1
#define IDC_CONFIGURE1 3
#define IDC_CONFIGURE2 4
#define IDD_CONFDLG 101
#define IDD_CONFIG 101
#define IDD_ABOUT 103
#define IDD_DLGMSD 106
#define IDD_DLGWASAPI 107
#define IDD_DLG_EYETOY 108
#define IDC_NAME 1000
#define IDC_LOGGING 1007
#define IDC_COMBO1 1008
#define IDC_COMBO2 1009
#define IDC_LIST1 1010
#define IDC_COMBO_API1 1010
#define IDC_COMBO3 1010
#define IDC_COMBO_FFB 1011
#define IDC_COMBO_API2 1011
#define IDC_BUTTON1 1012
#define IDC_DFP_PASS 1013
#define IDC_BUILD_DATE 1014
#define IDC_EDIT1 1015
#define IDC_COMBO_WHEEL_TYPE1 1037
#define IDC_COMBO_WHEEL_TYPE2 1038
#define IDC_SLIDER1 1039
#define IDC_BUFFER1 1040
#define IDC_COMBOMICAPI 1041
#define IDC_SLIDER2 1041
#define IDC_BUFFER2 1042
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 109
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1042
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

186
pcsx2/USB/configuration.cpp Normal file
View File

@ -0,0 +1,186 @@
#include "osdebugout.h"
#include "deviceproxy.h"
#include "configuration.h"
#include "shared/inifile.h"
#include <map>
#include <vector>
std::map<std::pair<int, std::string>, std::string> changedAPIs;
const TCHAR* iniFile = TEXT("USBqemu-wheel.ini");
static TSTDSTRING usb_path;
TSTDSTRING IniPath = TSTDSTRING(TEXT("./inis/")) + iniFile; // default path, just in case
TSTDSTRING LogDir;
CIniFile ciniFile;
EXPORT_C_(void) USBsetSettingsDir( const char* dir )
{
#ifdef _UNICODE
OSDebugOut(L"USBsetSettingsDir: %S\n", dir);
wchar_t dst[4096] = {0};
size_t num = 0;
mbstowcs_s(&num, dst, dir, countof(dst));
IniPath = dst;
IniPath.append(iniFile);
OSDebugOut(L"USBsetSettingsDir: %s\n", IniPath.c_str());
#else
IniPath = dir;
IniPath.append(iniFile);
#endif
}
EXPORT_C_(void) USBsetLogDir( const char* dir )
{
#ifdef _UNICODE
OSDebugOut(L"USBsetLogDir: %S\n", dir);
wchar_t dst[4096] = {0};
size_t num = 0;
mbstowcs_s(&num, dst, dir, countof(dst));
LogDir = dst;
LogDir.append(_T("USBqemu-wheel.log"));
#else
LogDir = dir;
#endif
}
std::string GetSelectedAPI(const std::pair<int, std::string>& pair)
{
auto it = changedAPIs.find(pair);
if (it != changedAPIs.end())
return it->second;
return std::string();
}
bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, TSTDSTRING& value)
{
CIniKey *key;
auto sect = ciniFile.GetSection(section);
if (sect && (key = sect->GetKey(param))) {
value = key->GetValue();
return true;
}
return false;
}
bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t& value)
{
CIniKey *key;
auto sect = ciniFile.GetSection(section);
if (sect && (key = sect->GetKey(param))) {
try {
value = std::stoi(key->GetValue());
return true;
}
catch (std::exception& err) {
OSDebugOut(TEXT("%" SFMTs "\n"), err.what());
}
}
return false;
}
bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const TSTDSTRING& value)
{
ciniFile.SetKeyValue(section, param, value);
return true;
}
bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t value)
{
ciniFile.SetKeyValue(section, param, TSTDTOSTRING(value));
return true;
}
#ifdef _UNICODE
bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, std::string& value)
{
char tmpA[4096] = { 0 };
size_t num = 0;
std::wstring str;
CIniKey *key;
auto sect = ciniFile.GetSection(section);
if (sect && (key = sect->GetKey(param))) {
str = key->GetValue();
wcstombs_s(&num, tmpA, str.c_str(), sizeof(tmpA)); //TODO error-check
value = tmpA;
return true;
}
return false;
}
bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const std::string& value)
{
std::wstring wstr;
wstr.assign(value.begin(), value.end());
ciniFile.SetKeyValue(section, param, wstr);
return true;
}
#endif
void SaveConfig() {
SaveSetting(_T("MAIN"), _T("log"), conf.Log);
SaveSetting(nullptr, 0, N_DEVICE_PORT, N_DEVICE, conf.Port[0]);
SaveSetting(nullptr, 1, N_DEVICE_PORT, N_DEVICE, conf.Port[1]);
SaveSetting(nullptr, 0, N_DEVICE_PORT, N_WHEEL_TYPE, conf.WheelType[0]);
SaveSetting(nullptr, 1, N_DEVICE_PORT, N_WHEEL_TYPE, conf.WheelType[1]);
for (auto& k : changedAPIs)
{
SaveSetting(nullptr, k.first.first, k.first.second, N_DEVICE_API, k.second);
}
bool ret = ciniFile.Save(IniPath);
OSDebugOut(_T("ciniFile.Save: %d [%s]\n"), ret, IniPath.c_str());
}
void LoadConfig() {
std::cerr << "USB load config\n" << std::endl;
ciniFile.Load(IniPath);
LoadSetting(_T("MAIN"), _T("log"), conf.Log);
LoadSetting(nullptr, 0, N_DEVICE_PORT, N_DEVICE, conf.Port[0]);
LoadSetting(nullptr, 1, N_DEVICE_PORT, N_DEVICE, conf.Port[1]);
LoadSetting(nullptr, 0, N_DEVICE_PORT, N_WHEEL_TYPE, conf.WheelType[0]);
LoadSetting(nullptr, 1, N_DEVICE_PORT, N_WHEEL_TYPE, conf.WheelType[1]);
auto& instance = RegisterDevice::instance();
for (int i=0; i<2; i++)
{
std::string api;
LoadSetting(nullptr, i, conf.Port[i], N_DEVICE_API, api);
auto dev = instance.Device(conf.Port[i]);
if (dev)
{
OSDebugOut(_T("Checking device '%" SFMTs "' api: '%" SFMTs "'...\n"), conf.Port[i].c_str(), api.c_str());
if (!dev->IsValidAPI(api))
{
api = "<invalid>";
const auto& apis = dev->ListAPIs();
if (!apis.empty())
api = *apis.begin();
OSDebugOut(_T("Invalid! Defaulting to '%" SFMTs "'\n"), api.c_str());
}
else
OSDebugOut(_T("API OK\n"));
}
if(api.size())
changedAPIs[std::make_pair(i, conf.Port[i])] = api;
}
}
void ClearSection(const TCHAR* section)
{
auto s = ciniFile.GetSection(section);
if (s) {
s->RemoveAllKeys();
}
}

166
pcsx2/USB/configuration.h Normal file
View File

@ -0,0 +1,166 @@
#pragma once
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include <vector>
#include <string>
#include <map>
#include <sstream>
#include "platcompat.h"
#include "osdebugout.h"
#define RESULT_CANCELED 0
#define RESULT_OK 1
#define RESULT_FAILED 2
// freeze modes:
#define FREEZE_LOAD 0
#define FREEZE_SAVE 1
#define FREEZE_SIZE 2
// Device-level config related defines
#define S_DEVICE_API TEXT("Device API")
#define S_WHEEL_TYPE TEXT("Wheel type")
#define S_DEVICE_PORT0 TEXT("Port 0")
#define S_DEVICE_PORT1 TEXT("Port 1")
#define S_CONFIG_PATH TEXT("Path")
#define N_DEVICE_API TEXT("device_api")
#define N_DEVICES TEXT("devices")
#define N_DEVICE TEXT("device")
#define N_WHEEL_PT TEXT("wheel_pt")
#define N_DEVICE_PORT0 TEXT("port_0")
#define N_DEVICE_PORT1 TEXT("port_1")
#define N_DEVICE_PORT "port"
#define N_WHEEL_TYPE0 TEXT("wheel_type_0")
#define N_WHEEL_TYPE1 TEXT("wheel_type_1")
#define N_WHEEL_TYPE TEXT("wheel_type")
#define N_CONFIG_PATH TEXT("path")
#define PLAYER_TWO_PORT 0
#define PLAYER_ONE_PORT 1
#define USB_PORT PLAYER_ONE_PORT
struct Config {
int Log;
std::string Port[2];
int WheelType[2];
Config();
};
extern Config conf;
void SaveConfig();
void LoadConfig();
void ClearSection(const TCHAR* section);
extern TSTDSTRING IniPath;
extern TSTDSTRING LogDir;
extern const TCHAR* iniFile;
extern std::map<std::pair<int /*port*/, std::string /*devname*/>, std::string> changedAPIs;
std::string GetSelectedAPI(const std::pair<int /*port*/, std::string /*devname*/>& pair);
bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, TSTDSTRING& value);
bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t& value);
bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const TSTDSTRING& value);
bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t value);
#ifdef _UNICODE
bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, std::string& value);
bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const std::string& value);
#endif
template<typename Type>
bool LoadSetting(const char* dev_type, int port, const std::string& key, const TCHAR* name, Type& var)
{
bool ret = false;
if (key.empty())
{
OSDebugOut(_T("Key is empty for '%s' on port %d\n"), name, port);
return false;
}
TSTDSTRING tkey;
tkey.assign(key.begin(), key.end());
TSTDSTRINGSTREAM section;
if (dev_type)
section << dev_type << _T(" ");
section << tkey << _T(" ") << port;
TSTDSTRING str = section.str();
OSDebugOut("[%s] '%s'=", str.c_str(), name);
ret = LoadSettingValue(IniPath, str, name, var);
if (ret)
OSDebugOutStream_noprfx(var);
else
OSDebugOut_noprfx("<failed>\n");
return ret;
}
template<typename Type>
bool LoadSetting(const TCHAR* section, const TCHAR* key, Type& var)
{
bool ret = false;
OSDebugOut("[%s] '%s'=", section, key);
ret = LoadSettingValue(IniPath, section, key, var);
if (ret)
OSDebugOutStream_noprfx(var);
else
OSDebugOut_noprfx("<failed>\n");
return ret;
}
/**
*
* [devices]
* portX = pad
*
* [pad X]
* api = joydev
*
* [joydev X]
* button0 = 1
* button1 = 2
* ...
*
* */
template<typename Type>
bool SaveSetting(const char* dev_type, int port, const std::string& key, const TCHAR* name, const Type var)
{
bool ret = false;
if (key.empty())
{
OSDebugOut(_T("Key is empty for '%s' on port %d\n"), name, port);
return false;
}
TSTDSTRING tkey;
tkey.assign(key.begin(), key.end());
TSTDSTRINGSTREAM section;
if (dev_type)
section << dev_type << _T(" ");
section << tkey << _T(" ") << port;
TSTDSTRING str = section.str();
OSDebugOut(_T("[%s] '%s'="), str.c_str(), name);
ret = SaveSettingValue(IniPath, str, name, var);
OSDebugOutStream_noprfx(var);
return ret;
}
template<typename Type>
bool SaveSetting(const TCHAR* section, const TCHAR* key, const Type var)
{
bool ret = false;
OSDebugOut(_T("[%s] '%s'="), section, key);
ret = SaveSettingValue(IniPath, section, key, var);
OSDebugOutStream_noprfx(var);
return ret;
}
#endif

35
pcsx2/USB/device_init.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "deviceproxy.h"
#include "usb-pad/usb-pad.h"
#include "usb-msd/usb-msd.h"
#include "usb-mic/usb-mic-singstar.h"
#include "usb-mic/usb-headset.h"
#include "usb-hid/usb-hid.h"
#include "usb-eyetoy/usb-eyetoy-webcam.h"
void RegisterDevice::Register()
{
auto& inst = RegisterDevice::instance();
inst.Add(DEVTYPE_PAD, new DeviceProxy<usb_pad::PadDevice>());
inst.Add(DEVTYPE_MSD, new DeviceProxy<usb_msd::MsdDevice>());
inst.Add(DEVTYPE_SINGSTAR, new DeviceProxy<usb_mic::SingstarDevice>());
inst.Add(DEVTYPE_LOGITECH_MIC, new DeviceProxy<usb_mic::LogitechMicDevice>());
inst.Add(DEVTYPE_LOGITECH_HEADSET, new DeviceProxy<usb_mic::HeadsetDevice>());
inst.Add(DEVTYPE_HIDKBD, new DeviceProxy<usb_hid::HIDKbdDevice>());
inst.Add(DEVTYPE_HIDMOUSE, new DeviceProxy<usb_hid::HIDMouseDevice>());
inst.Add(DEVTYPE_RBKIT, new DeviceProxy<usb_pad::RBDrumKitDevice>());
inst.Add(DEVTYPE_BUZZ, new DeviceProxy<usb_pad::BuzzDevice>());
inst.Add(DEVTYPE_EYETOY, new DeviceProxy<usb_eyetoy::EyeToyWebCamDevice>());
RegisterAPIs();
}
void RegisterDevice::Unregister()
{
/*for (auto& i: registerDeviceMap)
delete i.second;*/
registerDeviceMap.clear();
delete registerDevice;
registerDevice = nullptr;
UnregisterAPIs();
}

23
pcsx2/USB/deviceproxy.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "deviceproxy.h"
#include "usb-pad/padproxy.h"
#include "usb-mic/audiodeviceproxy.h"
#include "usb-hid/hidproxy.h"
#include "usb-eyetoy/videodeviceproxy.h"
RegisterDevice *RegisterDevice::registerDevice = nullptr;
void RegisterAPIs()
{
usb_pad::RegisterPad::Register();
usb_mic::RegisterAudioDevice::Register();
usb_hid::RegisterUsbHID::Register();
usb_eyetoy::RegisterVideoDevice::Register();
}
void UnregisterAPIs()
{
usb_pad::RegisterPad::instance().Clear();
usb_mic::RegisterAudioDevice::instance().Clear();
usb_hid::RegisterUsbHID::instance().Clear();
usb_eyetoy::RegisterVideoDevice::instance().Clear();
}

258
pcsx2/USB/deviceproxy.h Normal file
View File

@ -0,0 +1,258 @@
#ifndef DEVICEPROXY_H
#define DEVICEPROXY_H
#include "configuration.h"
#include <memory>
#include <string>
#include <map>
#include <list>
#include <algorithm>
#include <iterator>
//#include <memory>
#include "helpers.h"
#include "proxybase.h"
#include "qemu-usb/USBinternal.h"
void RegisterAPIs();
void UnregisterAPIs();
// also map key/array index
enum DeviceType
{
DEVTYPE_NONE = -1,
DEVTYPE_PAD = 0,
DEVTYPE_MSD,
DEVTYPE_SINGSTAR,
DEVTYPE_LOGITECH_MIC,
DEVTYPE_LOGITECH_HEADSET,
DEVTYPE_HIDKBD,
DEVTYPE_HIDMOUSE,
DEVTYPE_RBKIT,
DEVTYPE_BUZZ,
DEVTYPE_EYETOY,
};
struct SelectDeviceName {
template <typename S>
std::string operator()(const std::pair<const DeviceType, S> &x) const { return x.second->TypeName(); }
};
class DeviceError : public std::runtime_error
{
public:
DeviceError(const char* msg) : std::runtime_error(msg) {}
virtual ~DeviceError() {}
};
class DeviceProxyBase
{
public:
DeviceProxyBase() {};
virtual ~DeviceProxyBase() {}
virtual USBDevice* CreateDevice(int port) = 0;
virtual const TCHAR* Name() const = 0;
virtual const char* TypeName() const = 0;
virtual int Configure(int port, const std::string& api, void *data) = 0;
virtual std::list<std::string> ListAPIs() = 0;
virtual const TCHAR* LongAPIName(const std::string& name) = 0;
virtual int Freeze(int mode, USBDevice *dev, void *data) = 0;
virtual bool IsValidAPI(const std::string& api)
{
const std::list<std::string>& apis = ListAPIs();
auto it = std::find(apis.begin(), apis.end(), api);
if (it != apis.end())
return true;
return false;
}
};
template <class T>
class DeviceProxy : public DeviceProxyBase
{
public:
DeviceProxy() {}
virtual ~DeviceProxy()
{
OSDebugOut(TEXT("%p\n"), this);
}
virtual USBDevice* CreateDevice(int port)
{
return T::CreateDevice(port);
}
virtual const TCHAR* Name() const
{
return T::Name();
}
virtual const char* TypeName() const
{
return T::TypeName();
}
virtual int Configure(int port, const std::string& api, void *data)
{
return T::Configure(port, api, data);
}
virtual std::list<std::string> ListAPIs()
{
return T::ListAPIs();
}
virtual const TCHAR* LongAPIName(const std::string& name)
{
return T::LongAPIName(name);
}
virtual int Freeze(int mode, USBDevice *dev, void *data)
{
return T::Freeze(mode, dev, data);
}
};
template <class T>
class RegisterProxy
{
RegisterProxy(const RegisterProxy&) = delete;
RegisterProxy() {}
public:
typedef std::map<std::string, std::unique_ptr<T> > RegisterProxyMap;
static RegisterProxy& instance() {
static RegisterProxy registerProxy;
return registerProxy;
}
virtual ~RegisterProxy() { Clear(); OSDebugOut("%p\n", this); }
void Clear()
{
registerProxyMap.clear();
}
void Add(const std::string& name, T* creator)
{
registerProxyMap[name] = std::unique_ptr<T>(creator);
}
T* Proxy(const std::string& name)
{
return registerProxyMap[name].get();
}
std::list<std::string> Names() const
{
std::list<std::string> nameList;
std::transform(
registerProxyMap.begin(), registerProxyMap.end(),
std::back_inserter(nameList),
SelectKey());
return nameList;
}
std::string Name(int idx) const
{
auto it = registerProxyMap.begin();
std::advance(it, idx);
if (it != registerProxyMap.end())
return std::string(it->first);
return std::string();
}
const RegisterProxyMap& Map() const
{
return registerProxyMap;
}
private:
RegisterProxyMap registerProxyMap;
};
class RegisterDevice
{
RegisterDevice(const RegisterDevice&) = delete;
RegisterDevice() {}
static RegisterDevice *registerDevice;
public:
typedef std::map<DeviceType, std::unique_ptr<DeviceProxyBase> > RegisterDeviceMap;
static RegisterDevice& instance() {
if (!registerDevice)
registerDevice = new RegisterDevice();
return *registerDevice;
}
~RegisterDevice() { OSDebugOut("%p\n", this); }
static void Register();
void Unregister();
void Add(DeviceType key, DeviceProxyBase* creator)
{
registerDeviceMap[key] = std::unique_ptr<DeviceProxyBase>(creator);
}
DeviceProxyBase* Device(const std::string& name)
{
//return registerDeviceMap[name];
/*for (auto& k : registerDeviceMap)
if(k.first.name == name)
return k.second;
return nullptr;*/
auto proxy = std::find_if(registerDeviceMap.begin(),
registerDeviceMap.end(),
[&name](const RegisterDeviceMap::value_type& val) -> bool
{
return val.second->TypeName() == name;
});
if (proxy != registerDeviceMap.end())
return proxy->second.get();
return nullptr;
}
DeviceProxyBase* Device(int index)
{
auto it = registerDeviceMap.begin();
std::advance(it, index);
if (it != registerDeviceMap.end())
return it->second.get();
return nullptr;
}
DeviceType Index(const std::string& name)
{
auto proxy = std::find_if(registerDeviceMap.begin(),
registerDeviceMap.end(),
[&name](RegisterDeviceMap::value_type& val) -> bool
{
return val.second->TypeName() == name;
});
if (proxy != registerDeviceMap.end())
return proxy->first;
return DEVTYPE_NONE;
}
std::list<std::string> Names() const
{
std::list<std::string> nameList;
std::transform(
registerDeviceMap.begin(), registerDeviceMap.end(),
std::back_inserter(nameList),
SelectDeviceName());
return nameList;
}
std::string Name(int index) const
{
auto it = registerDeviceMap.begin();
std::advance(it, index);
if (it != registerDeviceMap.end())
return it->second->TypeName();
return std::string();
}
const RegisterDeviceMap& Map() const
{
return registerDeviceMap;
}
private:
RegisterDeviceMap registerDeviceMap;
};
#endif

518
pcsx2/USB/dynlink/pulse.cpp Normal file
View File

@ -0,0 +1,518 @@
#include "pulse.h"
#include <pulse/pulseaudio.h>
#include <dlfcn.h>
#include <iostream>
#include <atomic>
#if PA_CHECK_VERSION(12,99,1)
#define CONST const
#else
#define CONST
#endif
#define FUNDEFDECL(x) static decltype(&x) pfn_##x = nullptr
#define FUN_UNLOAD(fun) pfn_##fun = nullptr
#define FUN_LOAD(h,fun) \
pfn_##fun = (decltype(&fun))(dlsym(h, #fun)); \
if((error = dlerror()) != NULL) { \
std::cerr << error << std::endl; \
DynUnloadPulse(); \
return false; \
}
FUNDEFDECL(pa_usec_to_bytes);
FUNDEFDECL(pa_bytes_per_second);
FUNDEFDECL(pa_threaded_mainloop_start);
FUNDEFDECL(pa_threaded_mainloop_free);
FUNDEFDECL(pa_threaded_mainloop_stop);
FUNDEFDECL(pa_stream_unref);
FUNDEFDECL(pa_stream_disconnect);
FUNDEFDECL(pa_threaded_mainloop_new);
FUNDEFDECL(pa_threaded_mainloop_get_api);
FUNDEFDECL(pa_stream_set_read_callback);
FUNDEFDECL(pa_stream_connect_record);
FUNDEFDECL(pa_stream_new);
FUNDEFDECL(pa_stream_peek);
FUNDEFDECL(pa_strerror);
FUNDEFDECL(pa_stream_drop);
FUNDEFDECL(pa_context_connect);
FUNDEFDECL(pa_operation_unref);
FUNDEFDECL(pa_context_set_state_callback);
FUNDEFDECL(pa_context_get_state);
FUNDEFDECL(pa_mainloop_get_api);
FUNDEFDECL(pa_context_unref);
FUNDEFDECL(pa_context_disconnect);
FUNDEFDECL(pa_operation_get_state);
FUNDEFDECL(pa_context_get_source_info_list);
FUNDEFDECL(pa_mainloop_new);
FUNDEFDECL(pa_context_new);
FUNDEFDECL(pa_mainloop_iterate);
FUNDEFDECL(pa_mainloop_free);
FUNDEFDECL(pa_context_get_sink_info_list);
FUNDEFDECL(pa_stream_connect_playback);
FUNDEFDECL(pa_stream_set_write_callback);
FUNDEFDECL(pa_stream_begin_write);
FUNDEFDECL(pa_stream_cancel_write);
FUNDEFDECL(pa_stream_write);
FUNDEFDECL(pa_stream_get_state);
FUNDEFDECL(pa_stream_cork);
FUNDEFDECL(pa_stream_is_corked);
FUNDEFDECL(pa_stream_is_suspended);
FUNDEFDECL(pa_stream_set_state_callback);
FUNDEFDECL(pa_threaded_mainloop_lock);
FUNDEFDECL(pa_threaded_mainloop_unlock);
FUNDEFDECL(pa_threaded_mainloop_signal);
FUNDEFDECL(pa_threaded_mainloop_wait);
FUNDEFDECL(pa_sample_size);
FUNDEFDECL(pa_frame_size);
FUNDEFDECL(pa_stream_get_latency);
FUNDEFDECL(pa_stream_update_timing_info);
static void* pulse_handle = nullptr;
static std::atomic<int> refCntPulse (0);
//TODO Probably needs mutex somewhere, but PCSX2 usually inits pretty serially
bool DynLoadPulse()
{
const char* error = nullptr;
refCntPulse++;
if (pulse_handle && pfn_pa_mainloop_free)
return true;
//dlopen itself is refcounted too
pulse_handle = dlopen ("libpulse.so.0", RTLD_LAZY);
if (!pulse_handle) {
std::cerr << dlerror() << std::endl;
return false;
}
FUN_LOAD(pulse_handle, pa_stream_update_timing_info);
FUN_LOAD(pulse_handle, pa_stream_get_latency);
FUN_LOAD(pulse_handle, pa_usec_to_bytes);
FUN_LOAD(pulse_handle, pa_bytes_per_second);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_start);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_free);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_stop);
FUN_LOAD(pulse_handle, pa_stream_unref);
FUN_LOAD(pulse_handle, pa_stream_disconnect);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_new);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_get_api);
FUN_LOAD(pulse_handle, pa_stream_set_read_callback);
FUN_LOAD(pulse_handle, pa_stream_connect_record);
FUN_LOAD(pulse_handle, pa_stream_new);
FUN_LOAD(pulse_handle, pa_stream_peek);
FUN_LOAD(pulse_handle, pa_strerror);
FUN_LOAD(pulse_handle, pa_stream_drop);
FUN_LOAD(pulse_handle, pa_context_connect);
FUN_LOAD(pulse_handle, pa_operation_unref);
FUN_LOAD(pulse_handle, pa_context_set_state_callback);
FUN_LOAD(pulse_handle, pa_context_get_state);
FUN_LOAD(pulse_handle, pa_mainloop_get_api);
FUN_LOAD(pulse_handle, pa_context_unref);
FUN_LOAD(pulse_handle, pa_context_disconnect);
FUN_LOAD(pulse_handle, pa_operation_get_state);
FUN_LOAD(pulse_handle, pa_context_get_source_info_list);
FUN_LOAD(pulse_handle, pa_mainloop_new);
FUN_LOAD(pulse_handle, pa_context_new);
FUN_LOAD(pulse_handle, pa_mainloop_iterate);
FUN_LOAD(pulse_handle, pa_context_get_sink_info_list);
FUN_LOAD(pulse_handle, pa_stream_connect_playback);
FUN_LOAD(pulse_handle, pa_stream_set_write_callback);
FUN_LOAD(pulse_handle, pa_stream_begin_write);
FUN_LOAD(pulse_handle, pa_stream_cancel_write);
FUN_LOAD(pulse_handle, pa_stream_write);
FUN_LOAD(pulse_handle, pa_stream_get_state);
FUN_LOAD(pulse_handle, pa_stream_cork);
FUN_LOAD(pulse_handle, pa_stream_is_corked);
FUN_LOAD(pulse_handle, pa_stream_is_suspended);
FUN_LOAD(pulse_handle, pa_stream_set_state_callback);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_lock);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_unlock);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_signal);
FUN_LOAD(pulse_handle, pa_threaded_mainloop_wait);
FUN_LOAD(pulse_handle, pa_sample_size);
FUN_LOAD(pulse_handle, pa_frame_size);
FUN_LOAD(pulse_handle, pa_mainloop_free);
return true;
}
void DynUnloadPulse()
{
if (!pulse_handle && !pfn_pa_mainloop_free)
return;
if(!refCntPulse || --refCntPulse > 0)
return;
FUN_UNLOAD(pa_stream_update_timing_info);
FUN_UNLOAD(pa_stream_get_latency);
FUN_UNLOAD(pa_usec_to_bytes);
FUN_UNLOAD(pa_bytes_per_second);
FUN_UNLOAD(pa_threaded_mainloop_start);
FUN_UNLOAD(pa_threaded_mainloop_free);
FUN_UNLOAD(pa_threaded_mainloop_stop);
FUN_UNLOAD(pa_stream_unref);
FUN_UNLOAD(pa_stream_disconnect);
FUN_UNLOAD(pa_threaded_mainloop_new);
FUN_UNLOAD(pa_threaded_mainloop_get_api);
FUN_UNLOAD(pa_stream_set_read_callback);
FUN_UNLOAD(pa_stream_connect_record);
FUN_UNLOAD(pa_stream_new);
FUN_UNLOAD(pa_stream_peek);
FUN_UNLOAD(pa_strerror);
FUN_UNLOAD(pa_stream_drop);
FUN_UNLOAD(pa_context_connect);
FUN_UNLOAD(pa_operation_unref);
FUN_UNLOAD(pa_context_set_state_callback);
FUN_UNLOAD(pa_context_get_state);
FUN_UNLOAD(pa_mainloop_get_api);
FUN_UNLOAD(pa_context_unref);
FUN_UNLOAD(pa_context_disconnect);
FUN_UNLOAD(pa_operation_get_state);
FUN_UNLOAD(pa_context_get_source_info_list);
FUN_UNLOAD(pa_mainloop_new);
FUN_UNLOAD(pa_context_new);
FUN_UNLOAD(pa_mainloop_iterate);
FUN_UNLOAD(pa_context_get_sink_info_list);
FUN_UNLOAD(pa_stream_connect_playback);
FUN_UNLOAD(pa_stream_set_write_callback);
FUN_UNLOAD(pa_stream_begin_write);
FUN_UNLOAD(pa_stream_cancel_write);
FUN_UNLOAD(pa_stream_write);
FUN_UNLOAD(pa_stream_get_state);
FUN_UNLOAD(pa_stream_cork);
FUN_UNLOAD(pa_stream_is_corked);
FUN_UNLOAD(pa_stream_is_suspended);
FUN_UNLOAD(pa_stream_set_state_callback);
FUN_UNLOAD(pa_threaded_mainloop_lock);
FUN_UNLOAD(pa_threaded_mainloop_unlock);
FUN_UNLOAD(pa_threaded_mainloop_signal);
FUN_UNLOAD(pa_threaded_mainloop_wait);
FUN_UNLOAD(pa_sample_size);
FUN_UNLOAD(pa_frame_size);
FUN_UNLOAD(pa_mainloop_free);
dlclose(pulse_handle);
pulse_handle = nullptr;
}
#undef FUNDEFDECL
#undef FUN_LOAD
#undef FUN_UNLOAD
const char* pa_strerror(int error)
{
if (pfn_pa_strerror)
return pfn_pa_strerror(error);
return NULL;
}
int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api)
{
if (pfn_pa_context_connect)
return pfn_pa_context_connect(c, server, flags, api);
return PA_ERR_NOTIMPLEMENTED;
}
int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval)
{
if (pfn_pa_mainloop_iterate)
return pfn_pa_mainloop_iterate(m, block, retval);
return PA_ERR_NOTIMPLEMENTED;
}
int pa_stream_disconnect(pa_stream *s)
{
if (pfn_pa_stream_disconnect)
return pfn_pa_stream_disconnect(s);
return PA_ERR_NOTIMPLEMENTED;
}
int pa_stream_drop(pa_stream *p)
{
if (pfn_pa_stream_drop)
return pfn_pa_stream_drop(p);
return PA_ERR_NOTIMPLEMENTED;
}
int pa_threaded_mainloop_start(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_start)
return pfn_pa_threaded_mainloop_start(m);
return PA_ERR_NOTIMPLEMENTED;
}
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name)
{
if (pfn_pa_context_new)
return pfn_pa_context_new(mainloop, name);
return NULL;
}
pa_context_state_t pa_context_get_state(CONST pa_context *c)
{
if (pfn_pa_context_get_state)
return pfn_pa_context_get_state(c);
return PA_CONTEXT_FAILED;
}
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop *m)
{
if (pfn_pa_mainloop_get_api)
return pfn_pa_mainloop_get_api(m);
return NULL;
}
pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_get_api)
return pfn_pa_threaded_mainloop_get_api(m);
return NULL;
}
pa_mainloop *pa_mainloop_new(void)
{
if (pfn_pa_mainloop_new)
return pfn_pa_mainloop_new();
return NULL;
}
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata)
{
if (pfn_pa_context_get_source_info_list)
return pfn_pa_context_get_source_info_list(c, cb, userdata);
return NULL;
}
pa_operation_state_t pa_operation_get_state(CONST pa_operation *o)
{
if (pfn_pa_operation_get_state)
return pfn_pa_operation_get_state(o);
return PA_OPERATION_CANCELLED;
}
pa_threaded_mainloop *pa_threaded_mainloop_new(void)
{
if (pfn_pa_threaded_mainloop_new)
return pfn_pa_threaded_mainloop_new();
return NULL;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec)
{
if (pfn_pa_bytes_per_second)
return pfn_pa_bytes_per_second(spec);
return 0;
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec)
{
if (pfn_pa_usec_to_bytes)
return pfn_pa_usec_to_bytes(t, spec);
return 0;
}
void pa_context_disconnect(pa_context *c)
{
if (pfn_pa_context_disconnect)
pfn_pa_context_disconnect(c);
}
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata)
{
if (pfn_pa_context_set_state_callback)
pfn_pa_context_set_state_callback(c, cb, userdata);
}
void pa_context_unref(pa_context *c)
{
if (pfn_pa_context_unref)
pfn_pa_context_unref(c);
}
void pa_mainloop_free(pa_mainloop *m)
{
if (pfn_pa_mainloop_free)
pfn_pa_mainloop_free(m);
}
void pa_operation_unref(pa_operation *o)
{
if (pfn_pa_operation_unref)
pfn_pa_operation_unref(o);
}
void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata)
{
if (pfn_pa_stream_set_read_callback)
pfn_pa_stream_set_read_callback(p, cb, userdata);
}
void pa_stream_unref(pa_stream *s)
{
if (pfn_pa_stream_unref)
pfn_pa_stream_unref(s);
}
void pa_threaded_mainloop_free(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_free)
pfn_pa_threaded_mainloop_free(m);
}
void pa_threaded_mainloop_stop(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_stop)
pfn_pa_threaded_mainloop_stop(m);
}
int pa_stream_peek(pa_stream *p, const void **data, size_t *nbytes)
{
if (pfn_pa_stream_peek)
return pfn_pa_stream_peek(p, data, nbytes);
return -PA_ERR_NOTIMPLEMENTED;
}
pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map)
{
if (pfn_pa_stream_new)
return pfn_pa_stream_new(c, name, ss, map);
return NULL;
}
int pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags)
{
if (pfn_pa_stream_connect_record)
return pfn_pa_stream_connect_record(s, dev, attr, flags);
return PA_ERR_NOTIMPLEMENTED;
}
pa_operation* pa_context_get_sink_info_list(pa_context * c, pa_sink_info_cb_t cb, void * userdata)
{
if (pfn_pa_context_get_sink_info_list)
return pfn_pa_context_get_sink_info_list(c, cb, userdata);
return NULL;
}
int pa_stream_connect_playback(pa_stream *s, const char *dev,
const pa_buffer_attr *attr, pa_stream_flags_t flags,
const pa_cvolume *volume,
pa_stream *sync_stream)
{
if (pfn_pa_stream_connect_playback)
return pfn_pa_stream_connect_playback(s, dev, attr, flags, volume, sync_stream);
return PA_ERR_NOTIMPLEMENTED;
}
void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata)
{
if (pfn_pa_stream_set_write_callback)
pfn_pa_stream_set_write_callback(p, cb, userdata);
}
int pa_stream_begin_write(pa_stream *p, void **data, size_t *nbytes)
{
if (pfn_pa_stream_begin_write)
return pfn_pa_stream_begin_write(p, data, nbytes);
return PA_ERR_NOTIMPLEMENTED;
}
int pa_stream_cancel_write(pa_stream *p)
{
if (pfn_pa_stream_cancel_write)
return pfn_pa_stream_cancel_write(p);
return PA_ERR_NOTIMPLEMENTED;
}
int pa_stream_write(pa_stream *p, const void *data, size_t nbytes, pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek)
{
if (pfn_pa_stream_write)
return pfn_pa_stream_write(p, data, nbytes, free_cb, offset, seek);
return PA_ERR_NOTIMPLEMENTED;
}
pa_stream_state_t pa_stream_get_state(CONST pa_stream *p)
{
if (pfn_pa_stream_get_state)
return pfn_pa_stream_get_state(p);
return PA_STREAM_UNCONNECTED;
}
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata)
{
if (pfn_pa_stream_cork)
return pfn_pa_stream_cork(s, b, cb, userdata);
return NULL;
}
int pa_stream_is_corked(CONST pa_stream *s)
{
if (pfn_pa_stream_is_corked)
return pfn_pa_stream_is_corked (s);
return -PA_ERR_NOTIMPLEMENTED;
}
void pa_threaded_mainloop_lock(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_lock)
pfn_pa_threaded_mainloop_lock(m);
}
void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_unlock)
pfn_pa_threaded_mainloop_unlock(m);
}
void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept)
{
if (pfn_pa_threaded_mainloop_signal)
pfn_pa_threaded_mainloop_signal(m, wait_for_accept);
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m)
{
if (pfn_pa_threaded_mainloop_wait)
pfn_pa_threaded_mainloop_wait(m);
}
int pa_stream_is_suspended(CONST pa_stream *s)
{
if (pfn_pa_stream_is_suspended)
return pfn_pa_stream_is_suspended(s);
return -PA_ERR_NOTIMPLEMENTED;
}
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
if (pfn_pa_stream_set_state_callback)
pfn_pa_stream_set_state_callback(s, cb, userdata);
}
size_t pa_sample_size(const pa_sample_spec *spec)
{
if (pfn_pa_sample_size)
return pfn_pa_sample_size(spec);
return 0;
}
size_t pa_frame_size(const pa_sample_spec *spec)
{
if (pfn_pa_frame_size)
return pfn_pa_frame_size(spec);
return 0;
}
pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata)
{
if (pfn_pa_stream_update_timing_info)
return pfn_pa_stream_update_timing_info(p, cb, userdata);
return NULL;
}
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative)
{
if (pfn_pa_stream_get_latency)
return pfn_pa_stream_get_latency(s, r_usec, negative);
return -PA_ERR_NOTIMPLEMENTED;
}

View File

@ -0,0 +1,4 @@
#pragma once
bool DynLoadPulse();
void DynUnloadPulse();

3
pcsx2/USB/gtk.h Normal file
View File

@ -0,0 +1,3 @@
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include <gtk/gtk.h>

9
pcsx2/USB/helpers.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef HELPERS_H
#define HELPERS_H
struct SelectKey {
template <typename F, typename S>
F operator()(const std::pair<const F, S> &x) const { return x.first; }
};
#endif

View File

@ -0,0 +1,67 @@
#include "icon_buzz_24.h"
const unsigned char icon_buzz_24[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xa6, 0xf5,
0xfd, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd,
0xfd, 0xff, 0xfd, 0xf6, 0xa6, 0x39, 0x00, 0x00, 0x00,
0x00, 0x00, 0x50, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xd6, 0x50, 0x00, 0x00, 0x00, 0x1e, 0xce,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xce, 0x1e, 0x00, 0x00, 0x8b, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0x00,
0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x5e, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x5e, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x5e, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
0x00, 0x8b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x8b, 0x00, 0x00, 0x1e, 0xce,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xce, 0x1e, 0x00, 0x00, 0x00, 0x50, 0xd6, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x50, 0x00, 0x00,
0x00, 0x00, 0x00, 0x39, 0xa6, 0xf0, 0xfd, 0xff, 0xfe,
0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfd,
0xf0, 0xa6, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

2
pcsx2/USB/icon_buzz_24.h Normal file
View File

@ -0,0 +1,2 @@
#pragma once
extern const unsigned char icon_buzz_24[];

View File

@ -0,0 +1,220 @@
/* actualfile.c
* Copyright (C) 2002-2005 PCSX2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* PCSX2 members can be contacted through their website at www.pcsx2.net.
*/
#include <errno.h> // errno
#include <fcntl.h> // open()
#include <stdio.h> // rename()
#include <string.h> // strerror()
#include <sys/stat.h> // stat64(), open(), fstat()
#include <sys/types.h> // stat64(), open(), fstat(), lseek64()
#include <unistd.h> // stat64(), fstat(), lseek64(), read(), close(), write()
// unlink()
//#include "logfile.h"
#include "actualfile.h"
int IsActualFile(const char *filename) {
int retval;
struct stat64 filestat;
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: IsActualFile(%s)", filename);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
retval = stat64(filename, &filestat);
if((retval < 0) || (errno != 0)) {
#ifdef VERBOSE_WARNING_ACTUALFILE
PrintLog("USBqemu file: Error retrieving stats on %s", filename);
PrintLog("USBqemu file: %i:%s\n", errno, strerror(errno));
#endif /* VERBOSE_WARNING_ACTUALFILE */
return(-1); // Name doesn't exist.
} // ENDIF- Trouble getting stat on a file?
if(S_ISREG(filestat.st_mode) == 0) return(-2); // Not a regular file.
return(0); // Yep, that's a file.
} // END IsActualFile()
void ActualFileDelete(const char *filename) {
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileDelete(%s)", filename);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
unlink(filename);
} // END ActualFileDelete()
void ActualFileRename(const char *origname, const char *newname) {
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileRename(%s->%s)", origname, newname);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
rename(origname, newname);
return;
} // END ActualFileRename()
ACTUALHANDLE ActualFileOpenForRead(const char *filename) {
int newhandle;
if(filename == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileOpenForRead(%s)", filename);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
newhandle = open(filename, O_RDONLY | O_LARGEFILE);
if((newhandle < 0) || (errno != 0)) {
#ifdef VERBOSE_WARNING_ACTUALFILE
PrintLog("USBqemu file: Error opening file %s\n", filename);
PrintLog("USBqemu file: (%i) %i:%s\n", newhandle, errno, strerror(errno));
#endif /* VERBOSE_WARNING_ACTUALFILE */
return(-1);
} // ENDIF- Error? Abort
return(newhandle);
} // END ActualFileOpenForRead()
off64_t ActualFileSize(ACTUALHANDLE handle) {
int retval;
struct stat64 filestat;
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileSize()\n");
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
retval = fstat64(handle, &filestat);
if((retval < 0) || (errno != 0)) return(-1); // Name doesn't exist.
return(filestat.st_size);
} // END ActualFileSize()
int ActualFileSeek(ACTUALHANDLE handle, off64_t position) {
off64_t moved;
if(handle < 0) return(-1);
if(position < 0) return(-1); // Maybe... position = 0?
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileSeek(%lli)", position);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
moved = lseek64(handle, position, SEEK_SET);
if(errno != 0) {
#ifdef VERBOSE_WARNING_ACTUALFILE
PrintLog("USBqemu file: Error on seek (%lli)", position);
PrintLog("USBqemu file: %i:%s\n", errno, strerror(errno));
#endif /* VERBOSE_WARNING_ACTUALFILE */
return(-1);
} // ENDIF- Error? Abort
return(0);
} // END ActualFileSeek()
int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer) {
int retval;
if(handle == ACTUALHANDLENULL) return(-1);
if(bytes < 1) return(-1);
if(buffer == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileRead(%i)", bytes);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
retval = read(handle, buffer, bytes);
if((retval < 0) || (errno != 0)) {
#ifdef VERBOSE_WARNING_ACTUALFILE
PrintLog("USBqemu file: Error reading from file!");
PrintLog("USBqemu file: %i:%s", errno, strerror(errno));
#endif /* VERBOSE_WARNING_ACTUALFILE */
// return(-1);
} // ENDIF- Error? Abort
return(retval); // Send back how many bytes read
} // END ActualFileRead()
void ActualFileClose(ACTUALHANDLE handle) {
if(handle < 0) return;
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileClose()");
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
close(handle);
return;
} // END ActualFileClose()
ACTUALHANDLE ActualFileOpenForWrite(const char *filename) {
int newhandle;
if(filename == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileOpenForWrite(%s)", filename);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
newhandle = open(filename, O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
if((newhandle < 0) || (errno != 0)) {
#ifdef VERBOSE_WARNING_ACTUALFILE
PrintLog("USBqemu file: Error opening file %s", filename);
PrintLog("USBqemu file: (%i) %i:%s", newhandle, errno, strerror(errno));
#endif /* VERBOSE_WARNING_ACTUALFILE */
return(-1);
} // ENDIF- Error? Abort
return(newhandle);
} // END ActualFileOpenForWrite()
int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer) {
int retval;
if(handle < 0) return(-1);
if(bytes < 1) return(-1);
if(buffer == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_ACTUALFILE
PrintLog("USBqemu file: ActualFileWrite(%i)", bytes);
#endif /* VERBOSE_FUNCTION_ACTUALFILE */
errno = 0;
retval = write(handle, buffer, bytes);
if((retval < 0) || (errno != 0)) {
#ifdef VERBOSE_WARNING_ACTUALFILE
PrintLog("USBqemu file: Error writing to file!");
PrintLog("USBqemu file: %i:%s", errno, strerror(errno));
#endif /* VERBOSE_WARNING_ACTUALFILE */
// return(-1);
} // ENDIF- Error? Abort
return(retval); // Send back how many bytes written
} // END ActualFileWrite()

View File

@ -0,0 +1,48 @@
/* actualfile.h
* Copyright (C) 2002-2005 PCSX2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* PCSX2 members can be contacted through their website at www.pcsx2.net.
*/
#ifndef ACTUALFILE_H
#define ACTUALFILE_H
#include <sys/types.h> // off64_t
#define ACTUALHANDLE int
#define ACTUALHANDLENULL -1
// #define VERBOSE_FUNCTION_ACTUALFILE
// #define VERBOSE_WARNING_ACTUALFILE
extern int IsActualFile(const char *filename);
extern void ActualFileDelete(const char *filename);
extern void ActualFileRename(const char *origname, const char *newname);
extern ACTUALHANDLE ActualFileOpenForRead(const char *filename);
extern off64_t ActualFileSize(ACTUALHANDLE handle);
extern int ActualFileSeek(ACTUALHANDLE handle, off64_t position);
extern int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer);
extern void ActualFileClose(ACTUALHANDLE handle);
extern ACTUALHANDLE ActualFileOpenForWrite(const char *filename);
extern int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer);
#endif /* ACTUALFILE_H */

View File

@ -0,0 +1,333 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include <map>
#include <vector>
#include <string>
#include "gtk.h"
#include "../osdebugout.h"
#include "../configuration.h"
#include "../deviceproxy.h"
#include "../usb-pad/padproxy.h"
#include "../usb-mic/audiodeviceproxy.h"
#include "config.h"
// src/USB.cpp
extern bool configChanged;
struct SettingsCB
{
int player;
std::string device;
std::string api;
GtkComboBox* combo;
};
gboolean run_msg_dialog(gpointer data)
{
GtkWidget *dialog = (GtkWidget *)data;
gtk_widget_show_all (dialog);
gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_destroy (dialog);
return FALSE;
}
void SysMessage(const char *fmt, ...)
{
va_list list;
char msg[1024];
va_start (list, fmt);
vsnprintf (msg, sizeof(msg), fmt, list);
va_end (list);
if (msg[strlen(msg) - 1] == '\n')
msg[strlen(msg) - 1] = 0;
GtkWidget *dialog;
dialog = gtk_message_dialog_new (NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"%s", msg);
// run on main thread, a bit iffy
g_idle_add (run_msg_dialog, (gpointer)dialog);
}
static void wheeltypeChanged (GtkComboBox *widget, gpointer data)
{
gint idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
//if(data)
{
uint8_t port = MIN(reinterpret_cast<uintptr_t>(data), 1);
conf.WheelType[port] = idx;
OSDebugOut("Selected wheel type, port %d idx: %d\n", port, idx);
}
}
static void populateApiWidget(SettingsCB *settingsCB, const std::string& device)
{
gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (settingsCB->combo)));
auto dev = RegisterDevice::instance().Device( device );
int port = 1 - settingsCB->player;
GtkComboBox *widget = settingsCB->combo;
if (dev)
{
std::string api;
auto it = changedAPIs.find(std::make_pair(port, device));
if (it == changedAPIs.end())
{
LoadSetting(nullptr, port, device, N_DEVICE_API, api);
if (!dev->IsValidAPI(api))
api.clear();
}
else
api = it->second;
OSDebugOut("Current api: %s\n", api.c_str());
settingsCB->api = api;
int i = 0;
for(auto& it : dev->ListAPIs())
{
auto name = dev->LongAPIName(it);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), name);
if (api.size() && api == it)
gtk_combo_box_set_active (GTK_COMBO_BOX (widget), i);
else
gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
i++;
}
}
}
static void deviceChanged (GtkComboBox *widget, gpointer data)
{
SettingsCB *settingsCB = (SettingsCB *)data;
gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
int player = settingsCB->player;
std::string s;
if (active > 0)
s = RegisterDevice::instance().Name(active - 1);
settingsCB->device = s;
populateApiWidget(settingsCB, s);
if(player == 0)
conf.Port[1] = s;
else
conf.Port[0] = s;
OSDebugOut("Selected player %d idx: %d [%s]\n", player, active, s.c_str());
}
static void apiChanged (GtkComboBox *widget, gpointer data)
{
SettingsCB *settingsCB = (SettingsCB *)data;
int player = settingsCB->player;
gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
int port = 1 - player;
auto& name = settingsCB->device;
auto dev = RegisterDevice::instance().Device( name );
if (dev)
{
auto apis = dev->ListAPIs();
auto it = apis.begin();
std::advance(it, active);
if (it != apis.end())
{
auto pair = std::make_pair(port, name);
auto itAPI = changedAPIs.find(pair);
if (itAPI != changedAPIs.end())
itAPI->second = *it;
else
changedAPIs[pair] = *it;
settingsCB->api = *it;
OSDebugOut("selected api: %s\n", it->c_str());
}
}
}
static void configureApi (GtkWidget *widget, gpointer data)
{
SettingsCB *settingsCB = (SettingsCB *)data;
int player = settingsCB->player;
int port = 1 - player;
auto& name = settingsCB->device;
auto& api = settingsCB->api;
auto dev = RegisterDevice::instance().Device( name );
OSDebugOut("configure api %s [%s] for player %d\n", api.c_str(), name.c_str(), player);
if (dev)
{
GtkWidget *dlg = GTK_WIDGET (g_object_get_data (G_OBJECT(widget), "dlg"));
int res = dev->Configure(port, api, dlg);
OSDebugOut("Configure returned %d\n", res);
}
}
GtkWidget *new_combobox(const char* label, GtkWidget *vbox)
{
GtkWidget *ro_label, *rs_hbox, *rs_label, *rs_cb;
rs_hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), rs_hbox, FALSE, TRUE, 0);
rs_label = gtk_label_new (label);
gtk_box_pack_start (GTK_BOX (rs_hbox), rs_label, FALSE, TRUE, 5);
gtk_label_set_justify (GTK_LABEL (rs_label), GTK_JUSTIFY_RIGHT);
gtk_misc_set_alignment (GTK_MISC (rs_label), 1, 0.5);
rs_cb = gtk_combo_box_text_new ();
gtk_box_pack_start (GTK_BOX (rs_hbox), rs_cb, TRUE, TRUE, 5);
return rs_cb;
}
static GtkWidget* new_frame(const char *label, GtkWidget *box)
{
GtkWidget *ro_frame = gtk_frame_new (NULL);
gtk_box_pack_start (GTK_BOX (box), ro_frame, TRUE, FALSE, 0);
GtkWidget *ro_label = gtk_label_new (label);
gtk_frame_set_label_widget (GTK_FRAME (ro_frame), ro_label);
gtk_label_set_use_markup (GTK_LABEL (ro_label), TRUE);
GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
gtk_container_add (GTK_CONTAINER (ro_frame), vbox);
return vbox;
}
#ifdef __cplusplus
extern "C" {
#endif
void CALLBACK USBconfigure() {
RegisterDevice::Register();
LoadConfig();
void * that = NULL;
SettingsCB settingsCB[2];
settingsCB[0].player = 0;
settingsCB[1].player = 1;
const char* wt[] = {"Driving Force", "Driving Force Pro", "Driving Force Pro (rev11.02)", "GT Force"};
const char *players[] = {"Player 1:", "Player 2:"};
GtkWidget *rs_cb, *vbox;
uint32_t idx = 0, sel_idx = 0;
// Create the dialog window
GtkWidget *dlg = gtk_dialog_new_with_buttons (
"Qemu USB Settings", NULL, GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER);
gtk_window_set_resizable (GTK_WINDOW (dlg), TRUE);
GtkWidget *dlg_area_box = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
GtkWidget *main_vbox = gtk_vbox_new (FALSE, 5);
gtk_container_add (GTK_CONTAINER (dlg_area_box), main_vbox);
/*** Device type ***/
vbox = new_frame("Select device type:", main_vbox);
std::string devs[2] = { conf.Port[1], conf.Port[0] };
/*** Devices' Comboboxes ***/
for(int ply = 0; ply < 2; ply++)
{
settingsCB[ply].device = devs[ply];
rs_cb = new_combobox(players[ply], vbox);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rs_cb), "None");
gtk_combo_box_set_active (GTK_COMBO_BOX (rs_cb), 0);
auto devices = RegisterDevice::instance().Names();
int idx = 0, selected = 0;
for(auto& device : devices)
{
auto deviceProxy = RegisterDevice::instance().Device(device);
auto name = deviceProxy->Name();
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rs_cb), name );
idx++;
if (devs[ply] == device)
gtk_combo_box_set_active (GTK_COMBO_BOX (rs_cb), idx);
}
g_signal_connect (G_OBJECT (rs_cb), "changed", G_CALLBACK (deviceChanged), (gpointer)&settingsCB[ply]);
}
/*** APIs ***/
vbox = new_frame("Select device API:", main_vbox);
/*** API Comboboxes ***/
for(int ply = 0; ply < 2; ply++)
{
rs_cb = new_combobox (players[ply], vbox);
settingsCB[ply].combo = GTK_COMBO_BOX (rs_cb);
//gtk_combo_box_set_active (GTK_COMBO_BOX (rs_cb), sel_idx);
g_signal_connect (G_OBJECT (rs_cb), "changed", G_CALLBACK (apiChanged), (gpointer)&settingsCB[ply]);
GtkWidget *hbox = gtk_widget_get_parent (rs_cb);
GtkWidget *button = gtk_button_new_with_label ("Configure");
gtk_button_set_image(GTK_BUTTON (button), gtk_image_new_from_icon_name ("gtk-preferences", GTK_ICON_SIZE_BUTTON));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 5);
g_signal_connect (button, "clicked", G_CALLBACK (configureApi), (gpointer)&settingsCB[ply]);
g_object_set_data (G_OBJECT (button), "dlg", dlg);
populateApiWidget(&settingsCB[ply], devs[ply]);
}
/** Wheel type **/
vbox = new_frame("Emulated wheel model:", main_vbox);
for(int ply = 0; ply < 2; ply++)
{
int port = 1 - ply;
rs_cb = new_combobox (players[ply], vbox);
sel_idx = 0;
for (int i = 0; i < countof(wt); i++)
{
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rs_cb), wt[i]);
if(conf.WheelType[port] == i)
sel_idx = i;
}
gtk_combo_box_set_active (GTK_COMBO_BOX (rs_cb), sel_idx);
g_signal_connect (G_OBJECT (rs_cb), "changed", G_CALLBACK (wheeltypeChanged), reinterpret_cast<gpointer> (port));
}
gtk_widget_show_all (dlg);
// Modal loop
gint result = gtk_dialog_run (GTK_DIALOG (dlg));
gtk_widget_destroy (dlg);
// Wait for all gtk events to be consumed ...
while (gtk_events_pending ())
gtk_main_iteration_do (FALSE);
if (result == GTK_RESPONSE_OK)
{
SaveConfig();
configChanged = true;
}
// ClearAPIs();
}
void CALLBACK USBabout() {
}
#ifdef __cplusplus
} //extern "C"
#endif

View File

@ -0,0 +1,16 @@
#include "../osdebugout.h"
#include "../configuration.h"
#include "../deviceproxy.h"
#include "../usb-pad/padproxy.h"
#include "../usb-mic/audiodeviceproxy.h"
#include "config.h"
void SysMessage_stderr(const char *fmt, ...)
{
va_list arglist;
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
}

7
pcsx2/USB/linux/config.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef LINUXCONFIG_H
#define LINUXCONFIG_H
#include <sstream>
#include <string>
#include "osdebugout.h"
#endif

705
pcsx2/USB/linux/ini.c Normal file
View File

@ -0,0 +1,705 @@
/* ini.c
* Copyright (C) 2002-2005 PCSX2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* PCSX2 members can be contacted through their website at www.pcsx2.net.
*/
#include <stddef.h> // NULL
#include <stdio.h> // sprintf()
#include <stdarg.h> // va_start(), va_end(), vsprintf()
//#include "logfile.h"
#include "actualfile.h"
#include "ini.h"
//#define VERBOSE_FUNCTION_INI 1
const char INIext[] = ".ini";
const char INInewext[] = ".new";
#if VERBOSE_FUNCTION_INI
void PrintLog(const char *fmt, ...) {
char logfiletemp[2048];
va_list list;
int len;
va_start(list, fmt);
vsprintf(logfiletemp, fmt, list);
va_end(list);
len = 0;
while((len < 2048) && (logfiletemp[len] != 0)) len++;
if((len > 0) && (logfiletemp[len-1] == '\n')) len--;
if((len > 0) && (logfiletemp[len-1] == '\r')) len--;
logfiletemp[len] = 0; // Slice off the last "\r\n"...
fprintf(stderr, "%s\n", logfiletemp);
}
#endif
// Returns: position where new extensions should be added.
int INIRemoveExt(const char *argname, char *tempname) {
int i;
int j;
int k;
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: RemoveExt(%s)", argname);
#endif /* VERBOSE_FUNCTION_INI */
i = 0;
while((i <= INIMAXLEN) && (*(argname + i) != 0)) {
*(tempname + i) = *(argname + i);
i++;
} // ENDWHILE- Copying the argument name into a temporary area;
*(tempname + i) = 0; // And 0-terminate
k = i;
k--;
j = 0;
while((j <= INIMAXLEN) && (INIext[j] != 0)) j++;
j--;
while((j >= 0) && (*(tempname + k) == INIext[j])) {
k--;
j--;
} // ENDWHILE- Comparing the ending characters to the INI ext.
if(j < 0) {
k++;
i = k;
*(tempname + i) = 0; // 0-terminate, cutting off ".ini"
} // ENDIF- Do we have a match? Then remove the end chars.
return(i);
} // END INIRemoveExt()
void INIAddInExt(char *tempname, int temppos) {
int i;
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: AddInExt(%s, %i)", tempname, temppos);
#endif /* VERBOSE_FUNCTION_INI */
i = 0;
while((i + temppos < INIMAXLEN) && (INIext[i] != 0)) {
*(tempname + temppos + i) = INIext[i];
i++;
} // ENDWHILE- Attaching extenstion to filename
*(tempname + temppos + i) = 0; // And 0-terminate
} // END INIAddInExt()
void INIAddOutExt(char *tempname, int temppos) {
int i;
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: AddOutExt(%s, %i)", tempname, temppos);
#endif /* VERBOSE_FUNCTION_INI */
i = 0;
while((i + temppos < INIMAXLEN) && (INInewext[i] != 0)) {
*(tempname + temppos + i) = INInewext[i];
i++;
} // ENDWHILE- Attaching extenstion to filename
*(tempname + temppos + i) = 0; // And 0-terminate
} // END INIAddInExt()
// Returns number of bytes read to get line (0 means end-of-file)
int INIReadLine(ACTUALHANDLE infile, char *buffer) {
int charcount;
int i;
char tempin[2];
int retflag;
int retval;
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: ReadLine()");
#endif /* VERBOSE_FUNCTION_INI */
charcount = 0;
i = 0;
tempin[1] = 0;
retflag = 0;
while((i < INIMAXLEN) && (retflag < 2)) {
retval = ActualFileRead(infile, 1, tempin);
charcount++;
if(retval != 1) {
retflag = 2;
charcount--;
} else if(tempin[0] == '\n') {
retflag = 2;
} else if(tempin[0] >= ' ') {
*(buffer + i) = tempin[0];
i++;
} // ENDLONGIF- How do we react to the next character?
} // ENDWHILE- Loading up on characters until an End-of-Line appears
*(buffer + i) = 0; // And 0-terminate
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: Line: %s", buffer);
#endif /* VERBOSE_FUNCTION_INI */
return(charcount);
} // END INIReadLine()
// Note: Do we need to back-skip a char if something other \n follows \r?
// Returns: number of bytes to get to start of section (or -1)
int INIFindSection(ACTUALHANDLE infile, const char *section) {
int charcount;
int i;
int retflag;
int retval;
char scanbuffer[INIMAXLEN+1];
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: FindSection(%s)", section);
#endif /* VERBOSE_FUNCTION_INI */
charcount = 0;
retflag = 0;
while(retflag == 0) {
retval = INIReadLine(infile, scanbuffer);
if(retval == 0) return(-1); // EOF? Stop here.
if(scanbuffer[0] == '[') {
i = 0;
while((i < INIMAXLEN) &&
(*(section + i) != 0) &&
(*(section + i) == scanbuffer[i + 1])) i++;
if((i < INIMAXLEN - 2) && (*(section + i) == 0)) {
if((scanbuffer[i + 1] == ']') && (scanbuffer[i + 2] == 0)) {
retflag = 1;
} // ENDIF- End marks look good? Return successful.
} // ENDIF- Do we have a section match?
} // ENDIF- Does this look like a section header?
if(retflag == 0) charcount += retval;
} // ENDWHILE- Scanning lines for the correct [Section] header.
return(charcount);
} // END INIFindSection()
// Returns: number of bytes to get to start of keyword (or -1)
int INIFindKeyword(ACTUALHANDLE infile, const char *keyword, char *buffer) {
int charcount;
int i;
int j;
int retflag;
int retval;
char scanbuffer[INIMAXLEN+1];
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: FindKeyword(%s)", keyword);
#endif /* VERBOSE_FUNCTION_INI */
charcount = 0;
retflag = 0;
while(retflag == 0) {
retval = INIReadLine(infile, scanbuffer);
if(retval == 0) return(-1); // EOF? Stop here.
if(scanbuffer[0] == '[') return(-1); // New section? Stop here.
i = 0;
while((i < INIMAXLEN) &&
(*(keyword + i) != 0) &&
(*(keyword + i) == scanbuffer[i])) i++;
if((i < INIMAXLEN - 2) && (*(keyword + i) == 0)) {
if(scanbuffer[i] == '=') {
retflag = 1;
if(buffer != NULL) {
i++;
j = 0;
while((i < INIMAXLEN) && (scanbuffer[i] != 0)) {
*(buffer + j) = scanbuffer[i];
i++;
j++;
} // ENDWHILE- Copying the value out to the outbound buffer.
*(buffer + j) = 0; // And 0-terminate.
} // ENDIF- Return the value as well?
} // ENDIF- End marks look good? Return successful.
} // ENDIF- Do we have a section match?
if(retflag == 0) charcount += retval;
} // ENDWHILE- Scanning lines for the correct [Section] header.
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: Value: %s", buffer);
#endif /* VERBOSE_FUNCTION_INI */
return(charcount);
} // END INIFindKeyWord()
// Returns: number of bytes left to write... (from charcount back)
int INICopy(ACTUALHANDLE infile, ACTUALHANDLE outfile, int charcount) {
char buffer[4096];
int i;
int chunk;
int retval;
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: Copy(%i)", charcount);
#endif /* VERBOSE_FUNCTION_INI */
i = charcount;
chunk = 4096;
if(i < chunk) chunk = i;
while(chunk > 0) {
retval = ActualFileRead(infile, chunk, buffer);
if(retval <= 0) return(i); // Trouble? Stop here.
if(retval < chunk) chunk = retval; // Short block? Note it.
retval = ActualFileWrite(outfile, chunk, buffer);
if(retval <= 0) return(i); // Trouble? Stop here.
i -= retval;
if(retval < chunk) return(i); // Short block written? Stop here.
chunk = 4096;
if(i < chunk) chunk = i;
} // ENDWHILE- Copying a section of file across, one chunk at a time.
return(0);
} // END INICopyToPos()
int INISaveString(const char *file, const char *section, const char *keyword, const char *value) {
char inname[INIMAXLEN+1];
char outname[INIMAXLEN+1];
int filepos;
ACTUALHANDLE infile;
ACTUALHANDLE outfile;
int i;
int retval;
char templine[INIMAXLEN+1];
if(file == NULL) return(-1);
if(section == NULL) return(-1);
if(keyword == NULL) return(-1);
if(value == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: SaveString(%s, %s, %s, %s)",
file, section, keyword, value);
#endif /* VERBOSE_FUNCTION_INI */
filepos = INIRemoveExt(file, inname);
for(i = 0; i <= filepos; i++) outname[i] = inname[i];
INIAddInExt(inname, filepos);
INIAddOutExt(outname, filepos);
filepos = 0;
infile = ActualFileOpenForRead(inname);
if(infile == ACTUALHANDLENULL) {
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: creating new file");
#endif /* VERBOSE_FUNCTION_INI */
outfile = ActualFileOpenForWrite(inname);
if(outfile == ACTUALHANDLENULL) return(-1); // Just a bad name? Abort.
sprintf(templine, "[%s]\r\n", section);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
if(retval < i) {
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(inname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
sprintf(templine, "%s=%s\r\n", keyword, value);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
if(retval < i) {
ActualFileDelete(inname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
return(0);
} // ENDIF- No input file? Create a brand new .ini file then.
retval = INIFindSection(infile, section);
if(retval < 0) {
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: creating new section");
#endif /* VERBOSE_FUNCTION_INI */
outfile = ActualFileOpenForWrite(outname);
if(outfile == ACTUALHANDLENULL) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't open a temp file? Abort
ActualFileSeek(infile, 0); // Move ini to beginning of file...
INICopy(infile, outfile, 0x0FFFFFFF); // Copy the whole file out...
sprintf(templine, "\r\n[%s]\r\n", section);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
if(retval < i) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
sprintf(templine, "%s=%s\r\n", keyword, value);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
if(retval < i) {
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
ActualFileDelete(inname);
ActualFileRename(outname, inname);
return(0);
} // ENDIF- Couldn't find the section? Make a new one!
filepos = retval;
ActualFileSeek(infile, filepos);
filepos += INIReadLine(infile, templine); // Get section line's byte count
retval = INIFindKeyword(infile, keyword, NULL);
if(retval < 0) {
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: creating new keyword");
#endif /* VERBOSE_FUNCTION_INI */
ActualFileSeek(infile, filepos);
retval = INIReadLine(infile, templine);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++;
while((retval > 0) && (templine[i] == '=')) {
filepos += retval;
retval = INIReadLine(infile, templine);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++;
} // ENDWHILE- skimming to the bottom of the section
outfile = ActualFileOpenForWrite(outname);
if(outfile == ACTUALHANDLENULL) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't open a temp file? Abort
ActualFileSeek(infile, 0);
retval = INICopy(infile, outfile, filepos);
if(retval > 0) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing everything up to keyword? Abort.
sprintf(templine, "%s=%s\r\n", keyword, value);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
if(retval < i) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
} else {
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: replacing keyword");
#endif /* VERBOSE_FUNCTION_INI */
filepos += retval; // Position just before old version of keyword
outfile = ActualFileOpenForWrite(outname);
if(outfile == ACTUALHANDLENULL) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't open a temp file? Abort
ActualFileSeek(infile, 0);
retval = INICopy(infile, outfile, filepos);
if(retval > 0) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing everything up to keyword? Abort.
INIReadLine(infile, templine); // Read past old keyword/value...
// Replace with new value
sprintf(templine, "%s=%s\r\n", keyword, value);
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
if(retval < i) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
} // ENDIF- Need to add a new keyword?
INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(inname);
ActualFileRename(outname, inname);
return(0);
} // END INISaveString()
int INILoadString(const char *file, const char *section, const char *keyword, char *buffer) {
char inname[INIMAXLEN+1];
int filepos;
ACTUALHANDLE infile;
int retval;
if(file == NULL) return(-1);
if(section == NULL) return(-1);
if(keyword == NULL) return(-1);
if(buffer == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: LoadString(%s, %s, %s)",
file, section, keyword);
#endif /* VERBOSE_FUNCTION_INI */
filepos = INIRemoveExt(file, inname);
INIAddInExt(inname, filepos);
filepos = 0;
infile = ActualFileOpenForRead(inname);
if(infile == ACTUALHANDLENULL) return(-1);
retval = INIFindSection(infile, section);
if(retval < 0) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Didn't find it? Abort.
retval = INIFindKeyword(infile, keyword, buffer);
if(retval < 0) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Didn't find it? Abort.
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(0);
} // END INILoadString()
int INIRemove(const char *file, const char *section, const char *keyword) {
char inname[INIMAXLEN+1];
char outname[INIMAXLEN+1];
int filepos;
ACTUALHANDLE infile;
ACTUALHANDLE outfile;
char templine[INIMAXLEN+1];
int i;
int retval;
if(file == NULL) return(-1);
if(section == NULL) return(-1);
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: Remove(%s, %s, %s)",
file, section, keyword);
#endif /* VERBOSE_FUNCTION_INI */
filepos = INIRemoveExt(file, inname);
for(i = 0; i <= filepos; i++) outname[i] = inname[i];
INIAddInExt(inname, filepos);
INIAddOutExt(outname, filepos);
infile = ActualFileOpenForRead(inname);
if(infile == ACTUALHANDLENULL) return(-1);
retval = INIFindSection(infile, section);
if(retval == -1) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't even find the section? Abort
filepos = retval;
if(keyword == NULL) {
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: removing section");
#endif /* VERBOSE_FUNCTION_INI */
outfile = ActualFileOpenForWrite(outname);
if(outfile == ACTUALHANDLENULL) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't open a temp file? Abort
ActualFileSeek(infile, 0);
retval = INICopy(infile, outfile, filepos);
if(retval > 0) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing everything up to the section? Abort.
templine[0] = 0;
retval = 1;
while((retval > 0) && (templine[0] != '[')) {
retval = INIReadLine(infile, templine);
} // ENDWHILE- Read to the start of the next section... or EOF.
if(templine[0] == '[') {
i = 0;
while((i < INIMAXLEN) && (templine[i] != 0)) i++;
retval = ActualFileWrite(outfile, i, templine);
if(retval < i) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing it out? Abort.
} // ENDIF- Are there other sections after this one? Save them then.
} else {
filepos = retval;
ActualFileSeek(infile, filepos);
filepos += INIReadLine(infile, templine); // Get section line's byte count
retval = INIFindKeyword(infile, keyword, NULL);
if(retval == -1) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't find the keyword? Abort
filepos += retval;
#ifdef VERBOSE_FUNCTION_INI
PrintLog("USBqemu ini: removing keyword");
#endif /* VERBOSE_FUNCTION_INI */
outfile = ActualFileOpenForWrite(outname);
if(outfile == ACTUALHANDLENULL) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
return(-1);
} // ENDIF- Couldn't open a temp file? Abort
ActualFileSeek(infile, 0);
retval = INICopy(infile, outfile, filepos);
if(retval > 0) {
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(outname);
return(-1);
} // ENDIF- Trouble writing everything up to keyword? Abort.
INIReadLine(infile, templine); // Read (and discard) the keyword line
} // ENDIF- Wipe out the whole section? Or just a keyword?
INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file
ActualFileClose(infile);
infile = ACTUALHANDLENULL;
ActualFileClose(outfile);
outfile = ACTUALHANDLENULL;
ActualFileDelete(inname);
ActualFileRename(outname, inname);
return(0);
} // END INIRemove()
int INISaveUInt(const char *file, const char *section, const char *keyword, unsigned int value) {
char numvalue[INIMAXLEN+1];
sprintf(numvalue, "%u", value);
return(INISaveString(file, section, keyword, numvalue));
} // END INISaveUInt()
int INILoadUInt(const char *file, const char *section, const char *keyword, unsigned int *buffer) {
char numvalue[INIMAXLEN+1];
int retval;
unsigned int value;
// unsigned int sign; // Not needed in unsigned numbers
int pos;
if(buffer == NULL) return(-1);
*(buffer) = 0;
retval = INILoadString(file, section, keyword, numvalue);
if(retval < 0) return(retval);
value = 0;
// sign = 1; // Start positive
pos = 0;
// Note: skip leading spaces? (Shouldn't have to, I hope)
// if(numvalue[pos] == '-') {
// pos++;
// sign = -1;
// } // ENDIF- Negative sign check
while((pos < INIMAXLEN) && (numvalue[pos] != 0)) {
if(value > (0xFFFFFFFF / 10)) return(-1); // Overflow?
if((numvalue[pos] >= '0') && (numvalue[pos] <= '9')) {
value *= 10;
value += numvalue[pos] - '0';
pos++;
} else {
numvalue[pos] = 0;
} // ENDIF- Add a digit in? Or stop searching for digits?
} // ENDWHILE- Adding digits of info to our ever-increasing value
// value *= sign
*(buffer) = value;
return(0);
} // END INILoadUInt()

67
pcsx2/USB/linux/ini.h Normal file
View File

@ -0,0 +1,67 @@
/* ini.h
* Copyright (C) 2002-2005 PCSX2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* PCSX2 members can be contacted through their website at www.pcsx2.net.
*/
#ifndef INI_H
#define INI_H
// #ifndef __LINUX__
// #ifdef __linux__
// #define __LINUX__
// #endif /* __linux__ */
// #endif /* No __LINUX__ */
// #define CDVDdefs
// File format:
// [section]
// keyword=value
// file - Name of the INI file
// section - Section within the file
// keyword - Identifier for a value
// value - value to store with a keyword in a section in the file
// buffer - place to retrieve the value of a keyword
// return values: 0 = success, -1 = failure
// #define VERBOSE_FUNCTION_INI
#define INIMAXLEN 255
#if __cplusplus
extern "C" {
#endif
extern int INISaveString(const char *file, const char *section, const char *keyword, const char *value);
extern int INILoadString(const char *file, const char *section, const char *keyword, char *buffer);
extern int INISaveUInt(const char *file, const char *section, const char *keyword, unsigned int value);
extern int INILoadUInt(const char *file, const char *section, const char *keyword, unsigned int *buffer);
// NULL in the keyword below removes the whole section.
extern int INIRemove(const char *file, const char *section, const char *keyword);
#if __cplusplus
}
#endif
#endif /* INI_H */

16
pcsx2/USB/linux/util.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "util.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
bool file_exists(std::string path)
{
struct stat s;
return !stat(path.c_str(), &s) && !S_ISDIR(s.st_mode);
}
bool dir_exists(std::string path)
{
struct stat s;
return !stat(path.c_str(), &s) && S_ISDIR(s.st_mode);
}

5
pcsx2/USB/linux/util.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <string>
bool file_exists(std::string path);
bool dir_exists(std::string path);

47
pcsx2/USB/osdebugout.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "osdebugout.h"
std::wostream& operator<<(std::wostream& os, const std::string& s) {
std::wstring ws;
ws.assign(s.begin(), s.end());
return os << ws;
}
#ifdef _WIN32
static int rateLimit = 0;
void _OSDebugOut(const TCHAR *psz_fmt, ...)
{
if(rateLimit > 0 && rateLimit < 100)
{
rateLimit++;
return;
}
else
{
//rateLimit = 1;
}
va_list args;
va_start(args, psz_fmt);
#ifdef UNICODE
int bufsize = _vscwprintf(psz_fmt, args) + 1;
std::vector<WCHAR> msg(bufsize);
vswprintf_s(&msg[0], bufsize, psz_fmt, args);
#else
int bufsize = _vscprintf(psz_fmt, args) + 1;
std::vector<char> msg(bufsize);
vsprintf_s(&msg[0], bufsize, psz_fmt, args);
#endif
//_vsnwprintf_s(&msg[0], bufsize, bufsize-1, psz_fmt, args);
va_end(args);
//static FILE *hfile = nullptr;
//if (!hfile) {
// hfile = _wfopen(L"USBqemu-wheel.log", L"wb,ccs=UNICODE");
// if (!hfile) throw std::runtime_error("ass");
//}
//else
// fwprintf(hfile, L"%s", &msg[0]);
OutputDebugString(&msg[0]);
}
#endif

39
pcsx2/USB/osdebugout.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <cstdio>
#include <iostream>
#include <sstream>
#define USB_LOG __Log
void __Log(const char* fmt, ...);
#ifdef _WIN32
#include "platcompat.h"
#include <vector>
void _OSDebugOut(const TCHAR *psz_fmt, ...);
std::wostream& operator<<(std::wostream& os, const std::string& s);
#ifdef _DEBUG
#define OSDebugOut(psz_fmt, ...) _OSDebugOut(TEXT("[USBqemu] [%" SFMTs "]:%d\t") psz_fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define OSDebugOut_noprfx(psz_fmt, ...) _OSDebugOut(TEXT(psz_fmt), ##__VA_ARGS__)
#define OSDebugOutStream_noprfx(psz_str) do{ TSTDSTRINGSTREAM ss; ss << psz_str; _OSDebugOut(_T("%s\n"), ss.str().c_str()); }while(0)
#else
#define OSDebugOut(psz_fmt, ...) do{}while(0)
#define OSDebugOut_noprfx(psz_fmt, ...) do{}while(0)
#define OSDebugOutStream_noprfx(str) do{}while(0)
#endif
#else //_WIN32
#ifdef _DEBUG
#define OSDebugOut(psz_fmt, ...) do{ fprintf(stderr, "[USBqemu] [%s]:%d\t" psz_fmt, __func__, __LINE__, ##__VA_ARGS__); }while(0)
#define OSDebugOut_noprfx(psz_fmt, ...) do{ fprintf(stderr, psz_fmt, ##__VA_ARGS__); }while(0)
#define OSDebugOutStream_noprfx(str) do{ std::cerr << str << std::endl; }while(0)
#else
#define OSDebugOut(psz_fmt, ...) do{}while(0)
#define OSDebugOut_noprfx(psz_fmt, ...) do{}while(0)
#define OSDebugOutStream_noprfx(str) do{}while(0)
#endif
#endif //_WIN32

143
pcsx2/USB/platcompat.h Normal file
View File

@ -0,0 +1,143 @@
#ifndef PLATCOMPAT_H
#define PLATCOMPAT_H
// Annoying defines
// ---------------------------------------------------------------------
// make sure __POSIX__ is defined for all systems where we assume POSIX
// compliance
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__) || defined(__LINUX__)
#if !defined(__POSIX__)
#define __POSIX__ 1
#endif
#endif
#ifdef _WIN32
# define CALLBACK __stdcall
#else
# define CALLBACK __attribute__((stdcall))
#endif
#ifndef EXPORT_C_
#ifdef _MSC_VER
#define EXPORT_C_(type) extern "C" type CALLBACK
#else
#define EXPORT_C_(type) extern "C" __attribute__((stdcall,externally_visible,visibility("default"))) type
//#define EXPORT_C_(type) extern "C" __attribute__((stdcall,visibility("default"))) type
#endif
#endif
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#define wfopen _wfopen
#define fseeko64 _fseeki64
#define ftello64 _ftelli64
#define TSTDSTRING std::wstring
#define TSTDSTRINGSTREAM std::wstringstream
#define TSTDTOSTRING std::to_wstring
#ifdef _MSC_VER
typedef SSIZE_T ssize_t;
#endif
//FIXME narrow string fmt
#ifdef UNICODE
#define SFMTs "S"
#else
#define SFMTs "s"
#endif
#define __builtin_constant_p(p) false
#if _UNICODE
void SysMessageW(const wchar_t *fmt, ...);
#define SysMessage SysMessageW
#else
void SysMessageA(const char *fmt, ...);
#define SysMessage SysMessageA
#endif
#ifndef _T
#define _T(x) L##x
#endif
#else //_WIN32
#define MAX_PATH PATH_MAX
#define __inline inline
//#ifndef TEXT
//#define TEXT(x) L##x
//#endif
//FIXME narrow string fmt
#define SFMTs "s"
#define TEXT(val) val
#define _T(x) x
#define TCHAR char
#define wfopen fopen
#define TSTDSTRING std::string
#define TSTDSTRINGSTREAM std::stringstream
#define TSTDTOSTRING std::to_string
void SysMessage(const char *fmt, ...);
#endif //_WIN32
#if __MINGW32__
#define DBL_EPSILON 2.2204460492503131e-16
#define FLT_EPSILON 1.1920928955078125e-7f
template <size_t size>
errno_t mbstowcs_s(
size_t *pReturnValue,
wchar_t (&wcstr)[size],
const char *mbstr,
size_t count
)
{
return mbstowcs_s(pReturnValue, wcstr, size, mbstr, count);
}
template <size_t size>
errno_t wcstombs_s(
size_t *pReturnValue,
char (&mbstr)[size],
const wchar_t *wcstr,
size_t count
)
{
return wcstombs_s(pReturnValue, mbstr, size, wcstr, count);
}
#endif //__MINGW32__
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])))
#endif
#include <cstddef>
template <class T, std::size_t N>
constexpr std::size_t countof(const T (&)[N]) noexcept
{
return N;
}
template <class T>
constexpr std::size_t countof(const T N)
{
return N.size();
}
//TODO Idk, used only in desc.h and struct USBDescriptor should be already packed anyway
#if defined(_WIN32) && !defined(__MINGW32__)
#define PACK(def,name) __pragma( pack(push, 1) ) def name __pragma( pack(pop) )
#elif defined(__clang__)
#define PACK(def,name) def __attribute__((packed)) name
#else
#define PACK(def,name) def __attribute__((gcc_struct, packed)) name
#endif
#endif

10
pcsx2/USB/proxybase.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
//TODO Maybe too much inheritance?
class ProxyBase
{
public:
ProxyBase() {}
virtual ~ProxyBase() {}
virtual const TCHAR* Name() const = 0;
virtual int Configure(int port, const char* dev_type, void *data) = 0;
};

View File

@ -0,0 +1,287 @@
#ifndef USBINTERNAL_H
#define USBINTERNAL_H
#include "vl.h"
/* 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 // status regs from 0x0c54 but usb snooping
// reg is at 0x0c80, so only 11 ports?
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 OHCIState {
target_phys_addr_t mem_base;
int mem;
uint32_t 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;
int32_t 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];
/* Active packets. */
uint32_t old_ctl;
USBPacket usb_packet;
uint8_t usb_buf[8192];
uint32_t async_td;
bool async_complete;
} OHCIState;
/* Host Controller Communications Area */
struct ohci_hcca {
uint32_t intr[32];
uint16_t frame, pad;
uint32_t done;
};
// No offsetof in C++.
#ifndef offsetof
#define offsetof_(x,z) (intptr_t(&((x)->z)) - intptr_t(x))
#define offsetof(x,z) offsetof_((x*)0,z)
#endif
//ISO C++ forbids declaration of typeof with no type
/*
#define CONTAINER_OF(ptr, type, member) ({ \
const typeof(((type *) 0)->member) *__mptr = (ptr); \
(type *) ((char *) __mptr - offsetof(type, member));})
*/
#define CONTAINER_OF(p, type, field) ((type*) ((char*)p - ((ptrdiff_t)&((type*)0)->field)))
#define HCCA_WRITEBACK_OFFSET 128 //offsetof(struct ohci_hcca, frame)
#define HCCA_WRITEBACK_SIZE 8 /* frame, pad, done */
#define ED_WBACK_OFFSET 8 //offsetof(struct ohci_ed, head)
#define ED_WBACK_SIZE 4
/* Bitfields for the first word of an Endpoint Descriptor. */
#define OHCI_ED_FA_SHIFT 0 //device address
#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)
#define OHCI_ED_EN_SHIFT 7 //endpoint number
#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT)
#define OHCI_ED_D_SHIFT 11 //direction
#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT)
#define OHCI_ED_S (1<<13) //speed 0 - full, 1 - low
#define OHCI_ED_K (1<<14) //skip ED if 1
#define OHCI_ED_F (1<<15) //format 0 - inter, bulk or setup, 1 - isoch
//#define OHCI_ED_MPS_SHIFT 7
//#define OHCI_ED_MPS_MASK (0xf<<OHCI_ED_FA_SHIFT)
#define OHCI_ED_MPS_SHIFT 16 //max packet size
#define OHCI_ED_MPS_MASK (0x7ff<<OHCI_ED_MPS_SHIFT)
/* Flags in the head field of an Endpoint Descriptor. */
#define OHCI_ED_H 1 //halted
#define OHCI_ED_C 2
/* Bitfields for the first word of a Transfer Descriptor. */
#define OHCI_TD_R (1<<18)
#define OHCI_TD_DP_SHIFT 19
#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT)
#define OHCI_TD_DI_SHIFT 21
#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)
#define OHCI_TD_T0 (1<<24)
#define OHCI_TD_T1 (1<<24)
#define OHCI_TD_EC_SHIFT 26
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
#define OHCI_TD_CC_SHIFT 28
#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT)
/* Bitfields for the first word of an Isochronous Transfer Descriptor. */
/* CC & DI - same as in the General Transfer Descriptor */
#define OHCI_TD_SF_SHIFT 0
#define OHCI_TD_SF_MASK (0xffff<<OHCI_TD_SF_SHIFT)
#define OHCI_TD_FC_SHIFT 24
#define OHCI_TD_FC_MASK (7<<OHCI_TD_FC_SHIFT)
/* Isochronous Transfer Descriptor - Offset / PacketStatusWord */
#define OHCI_TD_PSW_CC_SHIFT 12
#define OHCI_TD_PSW_CC_MASK (0xf<<OHCI_TD_PSW_CC_SHIFT)
#define OHCI_TD_PSW_SIZE_SHIFT 0
#define OHCI_TD_PSW_SIZE_MASK (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
#define OHCI_PAGE_MASK 0xfffff000
#define OHCI_OFFSET_MASK 0xfff
#define OHCI_DPTR_MASK 0xfffffff0
#define OHCI_BM(val, field) \
(((val) & OHCI_##field##_MASK) >> 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;
};
/* Isochronous transfer descriptor */
struct ohci_iso_td {
uint32_t flags;
uint32_t bp;
uint32_t next;
uint32_t be;
uint16_t offset[8];
};
#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)) //TODO LSI has SOC at bits 16,17?
#define OHCI_INTR_SO (1U<<0) /* Scheduling overrun */
#define OHCI_INTR_WD (1U<<1) /* HcDoneHead writeback */
#define OHCI_INTR_SF (1U<<2) /* Start of frame */
#define OHCI_INTR_RD (1U<<3) /* Resume detect */
#define OHCI_INTR_UE (1U<<4) /* Unrecoverable error */
#define OHCI_INTR_FNO (1U<<5) /* Frame number overflow */
#define OHCI_INTR_RHSC (1U<<6) /* Root hub status change */
#define OHCI_INTR_OC (1U<<30) /* Ownership change */
#define OHCI_INTR_MIE (1U<<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 (1U<<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 (1U<<0)
#define OHCI_RHS_OCI (1U<<1)
#define OHCI_RHS_DRWE (1U<<15)
#define OHCI_RHS_LPSC (1U<<16)
#define OHCI_RHS_OCIC (1U<<17)
#define OHCI_RHS_CRWE (1U<<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 // the what?
#define OHCI_CC_DATAOVERRUN 0x8
#define OHCI_CC_DATAUNDERRUN 0x9
#define OHCI_CC_BUFFEROVERRUN 0xc
#define OHCI_CC_BUFFERUNDERRUN 0xd
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);
void ohci_hard_reset(OHCIState *ohci);
void ohci_soft_reset(OHCIState *ohci);
int ohci_bus_start(OHCIState *ohci);
void ohci_bus_stop(OHCIState *ohci);
#endif

131
pcsx2/USB/qemu-usb/bus.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "platcompat.h"
#include "USBinternal.h"
#include "vl.h"
#define USB_DEVICE_GET_CLASS(p) (&p->klass)
static void usb_device_realize(USBDevice *dev/*, Error **errp*/)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->realize) {
klass->realize(dev/*, errp*/);
}
}
USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->find_device) {
return klass->find_device(dev, addr);
}
return NULL;
}
static void usb_device_unrealize(USBDevice *dev/*, Error **errp*/)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->unrealize) {
klass->unrealize(dev/*, errp*/);
}
}
void usb_device_cancel_packet(USBDevice *dev, USBPacket *p)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->cancel_packet) {
klass->cancel_packet(dev, p);
}
}
void usb_device_handle_attach(USBDevice *dev)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_attach) {
klass->handle_attach(dev);
}
}
void usb_device_handle_reset(USBDevice *dev)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_reset) {
klass->handle_reset(dev);
}
}
void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
int value, int index, int length, uint8_t *data)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_control) {
klass->handle_control(dev, p, request, value, index, length, data);
}
}
void usb_device_handle_data(USBDevice *dev, USBPacket *p)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_data) {
klass->handle_data(dev, p);
}
}
/*const char *usb_device_get_product_desc(USBDevice *dev)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
return klass->product_desc;
}*/
const USBDesc *usb_device_get_usb_desc(USBDevice *dev)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (dev->usb_desc) {
return dev->usb_desc;
}
return klass->usb_desc;
}
void usb_device_set_interface(USBDevice *dev, int intf,
int alt_old, int alt_new)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->set_interface) {
klass->set_interface(dev, intf, alt_old, alt_new);
}
}
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->flush_ep_queue) {
klass->flush_ep_queue(dev, ep);
}
}
void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->ep_stopped) {
klass->ep_stopped(dev, ep);
}
}
int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps,
int streams)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->alloc_streams) {
return klass->alloc_streams(dev, eps, nr_eps, streams);
}
return 0;
}
void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->free_streams) {
klass->free_streams(dev, eps, nr_eps);
}
}

811
pcsx2/USB/qemu-usb/core.cpp Normal file
View File

@ -0,0 +1,811 @@
/*
* QEMU USB emulation
*
* Copyright (c) 2005 Fabrice Bellard
*
* 2008 Generic packet handler rewrite by Max Krasnyansky
*
* 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 "platcompat.h"
#include "osdebugout.h"
#include "vl.h"
#include "iov.h"
//#include "trace.h"
void usb_pick_speed(USBPort *port)
{
static const int speeds[] = {
//USB_SPEED_SUPER,
//USB_SPEED_HIGH,
USB_SPEED_FULL,
USB_SPEED_LOW,
};
USBDevice *udev = port->dev;
int i;
for (i = 0; i < ARRAY_SIZE(speeds); i++) {
if ((udev->speedmask & (1 << speeds[i])) &&
(port->speedmask & (1 << speeds[i]))) {
udev->speed = speeds[i];
return;
}
}
}
void usb_attach(USBPort *port)
{
USBDevice *dev = port->dev;
assert(dev != NULL);
assert(dev->attached);
assert(dev->state == USB_STATE_NOTATTACHED);
usb_pick_speed(port);
port->ops->attach(port);
dev->state = USB_STATE_ATTACHED;
usb_device_handle_attach(dev);
}
void usb_detach(USBPort *port)
{
USBDevice *dev = port->dev;
assert(dev != NULL);
assert(dev->state != USB_STATE_NOTATTACHED);
port->ops->detach(port);
dev->state = USB_STATE_NOTATTACHED;
}
void usb_reattach(USBPort *port)
{
usb_detach(port);
usb_attach(port);
}
void usb_port_reset(USBPort *port)
{
USBDevice *dev = port->dev;
OSDebugOut (TEXT("port %d\n"), port->index);
assert(dev != NULL);
usb_detach(port);
usb_attach(port);
usb_device_reset(dev);
}
void usb_device_reset(USBDevice *dev)
{
if (dev == NULL || !dev->attached) {
return;
}
dev->remote_wakeup = 0;
dev->addr = 0;
dev->state = USB_STATE_DEFAULT;
usb_device_handle_reset(dev);
}
void usb_wakeup(USBEndpoint *ep, unsigned int stream)
{
USBDevice *dev = ep->dev;
USBBus *bus = dev->bus; //usb_bus_from_device(dev);
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
dev->port->ops->wakeup(dev->port);
}
if (bus && bus->ops->wakeup_endpoint) {
bus->ops->wakeup_endpoint(bus, ep, stream);
}
}
/**********************/
/* 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_SETUP 1
#define SETUP_STATE_DATA 2
#define SETUP_STATE_ACK 3
#define SETUP_STATE_PARAM 4
static void do_token_setup(USBDevice *s, USBPacket *p)
{
int request, value, index;
if (p->iov.size != 8) {
p->status = USB_RET_STALL;
return;
}
usb_packet_copy(p, s->setup_buf, p->iov.size);
s->setup_index = 0;
p->actual_length = 0;
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
if (s->setup_len > sizeof(s->data_buf)) {
fprintf(stderr,
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
s->setup_len, sizeof(s->data_buf));
p->status = USB_RET_STALL;
return;
}
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
if (s->setup_buf[0] & USB_DIR_IN) {
usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
if (p->status == USB_RET_ASYNC) {
s->setup_state = SETUP_STATE_SETUP;
}
if (p->status != USB_RET_SUCCESS) {
return;
}
if (p->actual_length < s->setup_len) {
s->setup_len = p->actual_length;
}
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;
}
p->actual_length = 8;
}
static void do_token_in(USBDevice *s, USBPacket *p)
{
int request, value, index;
assert(p->ep->nr == 0);
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
switch(s->setup_state) {
case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
if (p->status == USB_RET_ASYNC) {
return;
}
s->setup_state = SETUP_STATE_IDLE;
p->actual_length = 0;
}
break;
case SETUP_STATE_DATA:
if (s->setup_buf[0] & USB_DIR_IN) {
int len = s->setup_len - s->setup_index;
if (len > p->iov.size) {
len = p->iov.size;
}
usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len;
if (s->setup_index >= s->setup_len) {
s->setup_state = SETUP_STATE_ACK;
}
return;
}
s->setup_state = SETUP_STATE_IDLE;
p->status = USB_RET_STALL;
break;
default:
p->status = USB_RET_STALL;
}
}
static void do_token_out(USBDevice *s, USBPacket *p)
{
assert(p->ep->nr == 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 additional output */
}
break;
case SETUP_STATE_DATA:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
int len = s->setup_len - s->setup_index;
if (len > p->iov.size) {
len = p->iov.size;
}
usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len;
if (s->setup_index >= s->setup_len) {
s->setup_state = SETUP_STATE_ACK;
}
return;
}
s->setup_state = SETUP_STATE_IDLE;
p->status = USB_RET_STALL;
break;
default:
p->status = USB_RET_STALL;
}
}
static void do_parameter(USBDevice *s, USBPacket *p)
{
int i, request, value, index;
for (i = 0; i < 8; i++) {
s->setup_buf[i] = p->parameter >> (i*8);
}
s->setup_state = SETUP_STATE_PARAM;
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
s->setup_index = 0;
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
if (s->setup_len > sizeof(s->data_buf)) {
fprintf(stderr,
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
s->setup_len, sizeof(s->data_buf));
p->status = USB_RET_STALL;
return;
}
if (p->pid == USB_TOKEN_OUT) {
usb_packet_copy(p, s->data_buf, s->setup_len);
}
usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
if (p->status == USB_RET_ASYNC) {
return;
}
if (p->actual_length < s->setup_len) {
s->setup_len = p->actual_length;
}
if (p->pid == USB_TOKEN_IN) {
p->actual_length = 0;
usb_packet_copy(p, s->data_buf, s->setup_len);
}
}
/* ctrl complete function for devices which use usb_generic_handle_packet and
may return USB_RET_ASYNC from their handle_control callback. Device code
which does this *must* call this function instead of the normal
usb_packet_complete to complete their async control packets. */
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
{
if (p->status < 0) {
s->setup_state = SETUP_STATE_IDLE;
}
switch (s->setup_state) {
case SETUP_STATE_SETUP:
if (p->actual_length < s->setup_len) {
s->setup_len = p->actual_length;
}
s->setup_state = SETUP_STATE_DATA;
p->actual_length = 8;
break;
case SETUP_STATE_ACK:
s->setup_state = SETUP_STATE_IDLE;
p->actual_length = 0;
break;
case SETUP_STATE_PARAM:
if (p->actual_length < s->setup_len) {
s->setup_len = p->actual_length;
}
if (p->pid == USB_TOKEN_IN) {
p->actual_length = 0;
usb_packet_copy(p, s->data_buf, s->setup_len);
}
break;
default:
break;
}
usb_packet_complete(s, p);
}
USBDevice *usb_find_device(USBPort *port, uint8_t addr)
{
USBDevice *dev = port->dev;
if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) {
return NULL;
}
if (dev->addr == addr) {
return dev;
}
return usb_device_find_device(dev, addr);
}
static void usb_process_one(USBPacket *p)
{
USBDevice *dev = p->ep->dev;
/*
* Handlers expect status to be initialized to USB_RET_SUCCESS, but it
* can be USB_RET_NAK here from a previous usb_process_one() call,
* or USB_RET_ASYNC from going through usb_queue_one().
*/
p->status = USB_RET_SUCCESS;
if (p->ep->nr == 0) {
/* control pipe */
if (p->parameter) {
do_parameter(dev, p);
return;
}
switch (p->pid) {
case USB_TOKEN_SETUP:
do_token_setup(dev, p);
break;
case USB_TOKEN_IN:
do_token_in(dev, p);
break;
case USB_TOKEN_OUT:
do_token_out(dev, p);
break;
default:
p->status = USB_RET_STALL;
}
} else {
/* data pipe */
usb_device_handle_data(dev, p);
}
}
static void usb_queue_one(USBPacket *p)
{
usb_packet_set_state(p, USB_PACKET_QUEUED);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
p->status = USB_RET_ASYNC;
}
/* Hand over a packet to a device for processing. p->status ==
USB_RET_ASYNC indicates the processing isn't finished yet, the
driver will call usb_packet_complete() when done processing it. */
void usb_handle_packet(USBDevice *dev, USBPacket *p)
{
if (dev == NULL) {
p->status = USB_RET_NODEV;
return;
}
assert(dev == p->ep->dev);
assert(dev->state == USB_STATE_DEFAULT);
usb_packet_check_state(p, USB_PACKET_SETUP);
assert(p->ep != NULL);
/* Submitting a new packet clears halt */
if (p->ep->halted) {
assert(QTAILQ_EMPTY(&p->ep->queue));
p->ep->halted = false;
}
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline || p->stream) {
usb_process_one(p);
if (p->status == USB_RET_ASYNC) {
/* hcd drivers cannot handle async for isoc */
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
/* using async for interrupt packets breaks migration */
assert(p->ep->type != USB_ENDPOINT_XFER_INT ||
(dev->flags & (1 << USB_DEV_FLAG_IS_HOST)));
usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else if (p->status == USB_RET_ADD_TO_QUEUE) {
usb_queue_one(p);
} else {
/*
* When pipelining is enabled usb-devices must always return async,
* otherwise packets can complete out of order!
*/
assert(p->stream || !p->ep->pipeline ||
QTAILQ_EMPTY(&p->ep->queue));
if (p->status != USB_RET_NAK) {
usb_packet_set_state(p, USB_PACKET_COMPLETE);
}
}
} else {
usb_queue_one(p);
}
}
void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
{
USBEndpoint *ep = p->ep;
assert(p->stream || QTAILQ_FIRST(&ep->queue) == p);
assert(p->status != USB_RET_ASYNC && p->status != USB_RET_NAK);
if (p->status != USB_RET_SUCCESS ||
(p->short_not_ok && (p->actual_length < p->iov.size))) {
ep->halted = true;
}
usb_packet_set_state(p, USB_PACKET_COMPLETE);
QTAILQ_REMOVE(&ep->queue, p, queue);
dev->port->ops->complete(dev->port, p);
}
/* Notify the controller that an async packet is complete. This should only
be called for packets previously deferred by returning USB_RET_ASYNC from
handle_packet. */
void usb_packet_complete(USBDevice *dev, USBPacket *p)
{
USBEndpoint *ep = p->ep;
usb_packet_check_state(p, USB_PACKET_ASYNC);
usb_packet_complete_one(dev, p);
while (!QTAILQ_EMPTY(&ep->queue)) {
p = QTAILQ_FIRST(&ep->queue);
if (ep->halted) {
/* Empty the queue on a halt */
p->status = USB_RET_REMOVE_FROM_QUEUE;
dev->port->ops->complete(dev->port, p);
continue;
}
if (p->state == USB_PACKET_ASYNC) {
break;
}
usb_packet_check_state(p, USB_PACKET_QUEUED);
usb_process_one(p);
if (p->status == USB_RET_ASYNC) {
usb_packet_set_state(p, USB_PACKET_ASYNC);
break;
}
usb_packet_complete_one(ep->dev, p);
}
}
/* Cancel an active packet. The packed must have been deferred by
returning USB_RET_ASYNC from handle_packet, and not yet
completed. */
void usb_cancel_packet(USBPacket * p)
{
bool callback = (p->state == USB_PACKET_ASYNC);
assert(usb_packet_is_inflight(p));
usb_packet_set_state(p, USB_PACKET_CANCELED);
QTAILQ_REMOVE(&p->ep->queue, p, queue);
if (callback) {
usb_device_cancel_packet(p->ep->dev, p);
}
}
void usb_packet_init(USBPacket *p)
{
qemu_iovec_init(&p->iov, 1);
}
static const char *usb_packet_state_name(USBPacketState state)
{
static const char *name[] = {
/*[USB_PACKET_UNDEFINED] =*/ "undef",
/*[USB_PACKET_SETUP] =*/ "setup",
/*[USB_PACKET_QUEUED] =*/ "queued",
/*[USB_PACKET_ASYNC] =*/ "async",
/*[USB_PACKET_COMPLETE] =*/ "complete",
/*[USB_PACKET_CANCELED] =*/ "canceled",
};
if (state < ARRAY_SIZE(name)) {
return name[state];
}
return "INVALID";
}
void usb_packet_check_state(USBPacket *p, USBPacketState expected)
{
USBDevice *dev;
USBBus *bus;
if (p->state == expected) {
return;
}
dev = p->ep->dev;
bus = dev->bus; //usb_bus_from_device(dev);
//trace_usb_packet_state_fault(bus->busnr, dev->port->path, p->ep->nr, p,
// usb_packet_state_name(p->state),
// usb_packet_state_name(expected));
assert(!"usb packet state check failed");
}
void usb_packet_set_state(USBPacket *p, USBPacketState state)
{
/*if (p->ep) {
USBDevice *dev = p->ep->dev;
USBBus *bus = usb_bus_from_device(dev);
trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
usb_packet_state_name(p->state),
usb_packet_state_name(state));
} else {
trace_usb_packet_state_change(-1, "", -1, p,
usb_packet_state_name(p->state),
usb_packet_state_name(state));
}*/
p->state = state;
}
void usb_packet_setup(USBPacket *p, int pid,
USBEndpoint *ep, unsigned int stream,
uint64_t id, bool short_not_ok, bool int_req)
{
assert(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL);
p->id = id;
p->pid = pid;
p->ep = ep;
p->stream = stream;
p->status = USB_RET_SUCCESS;
p->actual_length = 0;
p->parameter = 0;
p->short_not_ok = short_not_ok;
p->int_req = int_req;
p->combined = NULL;
qemu_iovec_reset(&p->iov);
usb_packet_set_state(p, USB_PACKET_SETUP);
}
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
{
qemu_iovec_add(&p->iov, ptr, len);
}
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
{
QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
assert(bytes <= INT_MAX);
assert(p->actual_length >= 0);
assert(p->actual_length + bytes <= iov->size);
switch (p->pid) {
case USB_TOKEN_SETUP:
case USB_TOKEN_OUT:
iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
break;
case USB_TOKEN_IN:
iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
break;
default:
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
abort();
}
p->actual_length += bytes;
}
void usb_packet_skip(USBPacket *p, size_t bytes)
{
QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
assert(bytes <= INT_MAX);
assert(p->actual_length >= 0);
assert(p->actual_length + bytes <= iov->size);
if (p->pid == USB_TOKEN_IN) {
iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes);
}
p->actual_length += bytes;
}
size_t usb_packet_size(USBPacket *p)
{
return p->combined ? p->combined->iov.size : p->iov.size;
}
void usb_packet_cleanup(USBPacket *p)
{
assert(!usb_packet_is_inflight(p));
qemu_iovec_destroy(&p->iov);
}
void usb_ep_reset(USBDevice *dev)
{
int ep;
dev->ep_ctl.nr = 0;
dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
dev->ep_ctl.ifnum = 0;
dev->ep_ctl.max_packet_size = 64;
dev->ep_ctl.max_streams = 0;
dev->ep_ctl.dev = dev;
dev->ep_ctl.pipeline = false;
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
dev->ep_in[ep].nr = ep + 1;
dev->ep_out[ep].nr = ep + 1;
dev->ep_in[ep].pid = USB_TOKEN_IN;
dev->ep_out[ep].pid = USB_TOKEN_OUT;
dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID;
dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID;
dev->ep_in[ep].max_packet_size = 0;
dev->ep_out[ep].max_packet_size = 0;
dev->ep_in[ep].max_streams = 0;
dev->ep_out[ep].max_streams = 0;
dev->ep_in[ep].dev = dev;
dev->ep_out[ep].dev = dev;
dev->ep_in[ep].pipeline = false;
dev->ep_out[ep].pipeline = false;
}
}
void usb_ep_init(USBDevice *dev)
{
int ep;
usb_ep_reset(dev);
QTAILQ_INIT(&dev->ep_ctl.queue);
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
QTAILQ_INIT(&dev->ep_in[ep].queue);
QTAILQ_INIT(&dev->ep_out[ep].queue);
}
}
void usb_ep_dump(USBDevice *dev)
{
static const char *tname[] = {
/* [USB_ENDPOINT_XFER_CONTROL] = */ "control",
/* [USB_ENDPOINT_XFER_ISOC] = */ "isoc",
/* [USB_ENDPOINT_XFER_BULK] = */ "bulk",
/* [USB_ENDPOINT_XFER_INT] = */ "int",
};
int ifnum, ep, first;
fprintf(stderr, "Device \"%s\", config %d\n",
dev->product_desc, dev->configuration);
for (ifnum = 0; ifnum < 16; ifnum++) {
first = 1;
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID &&
dev->ep_in[ep].ifnum == ifnum) {
if (first) {
first = 0;
fprintf(stderr, " Interface %d, alternative %d\n",
ifnum, dev->altsetting[ifnum]);
}
fprintf(stderr, " Endpoint %d, IN, %s, %d max\n", ep,
tname[dev->ep_in[ep].type],
dev->ep_in[ep].max_packet_size);
}
if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID &&
dev->ep_out[ep].ifnum == ifnum) {
if (first) {
first = 0;
fprintf(stderr, " Interface %d, alternative %d\n",
ifnum, dev->altsetting[ifnum]);
}
fprintf(stderr, " Endpoint %d, OUT, %s, %d max\n", ep,
tname[dev->ep_out[ep].type],
dev->ep_out[ep].max_packet_size);
}
}
}
fprintf(stderr, "--\n");
}
struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep)
{
struct USBEndpoint *eps;
if (dev == NULL) {
return NULL;
}
eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out;
if (ep == 0) {
return &dev->ep_ctl;
}
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
return eps + ep - 1;
}
uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
return uep->type;
}
void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->type = type;
}
void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->ifnum = ifnum;
}
void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
int size, microframes;
size = raw & 0x7ff;
switch ((raw >> 11) & 3) {
case 1:
microframes = 2;
break;
case 2:
microframes = 3;
break;
default:
microframes = 1;
break;
}
uep->max_packet_size = size * microframes;
}
void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
int MaxStreams;
MaxStreams = raw & 0x1f;
if (MaxStreams) {
uep->max_streams = 1 << MaxStreams;
} else {
uep->max_streams = 0;
}
}
void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->halted = halted;
}
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
USBPacket *p;
QTAILQ_FOREACH(p, &uep->queue, queue) {
if (p->id == id) {
return p;
}
}
return NULL;
}
void usb_wakeup(USBDevice *dev)
{
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
dev->port->ops->wakeup(dev->port);
}
}

851
pcsx2/USB/qemu-usb/desc.cpp Normal file
View File

@ -0,0 +1,851 @@
#include "platcompat.h"
#include "vl.h"
#include "desc.h"
#include "glib.h"
#include "osdebugout.h"
//#include "trace.h"
/* ------------------------------------------------------------------ */
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
bool msos, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x12;
USBDescriptor *d = (USBDescriptor *)dest;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_DEVICE;
if (msos && dev->bcdUSB < 0x0200) {
/*
* Version 2.0+ required for microsoft os descriptors to work.
* Done this way so msos-desc compat property will handle both
* the version and the new descriptors being present.
*/
d->u.device.bcdUSB_lo = usb_lo(0x0200);
d->u.device.bcdUSB_hi = usb_hi(0x0200);
} else {
d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB);
d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB);
}
d->u.device.bDeviceClass = dev->bDeviceClass;
d->u.device.bDeviceSubClass = dev->bDeviceSubClass;
d->u.device.bDeviceProtocol = dev->bDeviceProtocol;
d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0;
d->u.device.idVendor_lo = usb_lo(id->idVendor);
d->u.device.idVendor_hi = usb_hi(id->idVendor);
d->u.device.idProduct_lo = usb_lo(id->idProduct);
d->u.device.idProduct_hi = usb_hi(id->idProduct);
d->u.device.bcdDevice_lo = usb_lo(id->bcdDevice);
d->u.device.bcdDevice_hi = usb_hi(id->bcdDevice);
d->u.device.iManufacturer = id->iManufacturer;
d->u.device.iProduct = id->iProduct;
d->u.device.iSerialNumber = id->iSerialNumber;
d->u.device.bNumConfigurations = dev->bNumConfigurations;
return bLength;
}
int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x0a;
USBDescriptor *d = (USBDescriptor *)dest;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
d->u.device_qualifier.bcdUSB_lo = usb_lo(dev->bcdUSB);
d->u.device_qualifier.bcdUSB_hi = usb_hi(dev->bcdUSB);
d->u.device_qualifier.bDeviceClass = dev->bDeviceClass;
d->u.device_qualifier.bDeviceSubClass = dev->bDeviceSubClass;
d->u.device_qualifier.bDeviceProtocol = dev->bDeviceProtocol;
d->u.device_qualifier.bMaxPacketSize0 = dev->bMaxPacketSize0;
d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations;
d->u.device_qualifier.bReserved = 0;
return bLength;
}
int usb_desc_config(const USBDescConfig& conf, int flags,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
uint16_t wTotalLength = 0;
USBDescriptor *d = (USBDescriptor *)dest;
int rc;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_CONFIG;
d->u.config.bNumInterfaces = conf.bNumInterfaces;
d->u.config.bConfigurationValue = conf.bConfigurationValue;
d->u.config.iConfiguration = conf.iConfiguration;
d->u.config.bmAttributes = conf.bmAttributes;
d->u.config.bMaxPower = conf.bMaxPower;
wTotalLength += bLength;
/* handle grouped interfaces if any */
for (auto& i : conf.if_groups) {
rc = usb_desc_iface_group(i, flags,
dest + wTotalLength,
len - wTotalLength);
if (rc < 0) {
return rc;
}
wTotalLength += rc;
}
/* handle normal (ungrouped / no IAD) interfaces if any */
for (auto& i : conf.ifs) {
rc = usb_desc_iface(i, flags,
dest + wTotalLength, len - wTotalLength);
if (rc < 0) {
return rc;
}
wTotalLength += rc;
}
d->u.config.wTotalLength_lo = usb_lo(wTotalLength);
d->u.config.wTotalLength_hi = usb_hi(wTotalLength);
return wTotalLength;
}
int usb_desc_iface_group(const USBDescIfaceAssoc& iad, int flags,
uint8_t *dest, size_t len)
{
int pos = 0;
int i = 0;
/* handle interface association descriptor */
uint8_t bLength = 0x08;
if (len < bLength) {
return -1;
}
dest[0x00] = bLength;
dest[0x01] = USB_DT_INTERFACE_ASSOC;
dest[0x02] = iad.bFirstInterface;
dest[0x03] = iad.bInterfaceCount;
dest[0x04] = iad.bFunctionClass;
dest[0x05] = iad.bFunctionSubClass;
dest[0x06] = iad.bFunctionProtocol;
dest[0x07] = iad.iFunction;
pos += bLength;
/* handle associated interfaces in this group */
for (auto& i : iad.ifs) {
int rc = usb_desc_iface(i, flags, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
pos += rc;
}
return pos;
}
int usb_desc_iface(const USBDescIface& iface, int flags,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
int rc, pos = 0;
USBDescriptor *d = (USBDescriptor *)dest;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_INTERFACE;
d->u.intf.bInterfaceNumber = iface.bInterfaceNumber;
d->u.intf.bAlternateSetting = iface.bAlternateSetting;
d->u.intf.bNumEndpoints = iface.bNumEndpoints;
d->u.intf.bInterfaceClass = iface.bInterfaceClass;
d->u.intf.bInterfaceSubClass = iface.bInterfaceSubClass;
d->u.intf.bInterfaceProtocol = iface.bInterfaceProtocol;
d->u.intf.iInterface = iface.iInterface;
pos += bLength;
for (auto& i : iface.descs) {
rc = usb_desc_other(i, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
pos += rc;
}
for (auto& i : iface.eps) {
rc = usb_desc_endpoint(i, flags, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
pos += rc;
}
return pos;
}
int usb_desc_endpoint(const USBDescEndpoint& ep, int flags,
uint8_t *dest, size_t len)
{
uint8_t bLength = ep.is_audio ? 0x09 : 0x07;
uint8_t extralen = ep.extra ? ep.extra[0] : 0;
uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0;
USBDescriptor *d = (USBDescriptor *)dest;
if (len < bLength + extralen + superlen) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_ENDPOINT;
d->u.endpoint.bEndpointAddress = ep.bEndpointAddress;
d->u.endpoint.bmAttributes = ep.bmAttributes;
d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep.wMaxPacketSize);
d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep.wMaxPacketSize);
d->u.endpoint.bInterval = ep.bInterval;
if (ep.is_audio) {
d->u.endpoint.bRefresh = ep.bRefresh;
d->u.endpoint.bSynchAddress = ep.bSynchAddress;
}
if (superlen) {
USBDescriptor *d = (USBDescriptor *)(dest + bLength);
d->bLength = 0x06;
d->bDescriptorType = USB_DT_ENDPOINT_COMPANION;
d->u.super_endpoint.bMaxBurst = ep.bMaxBurst;
d->u.super_endpoint.bmAttributes = ep.bmAttributes_super;
d->u.super_endpoint.wBytesPerInterval_lo =
usb_lo(ep.wBytesPerInterval);
d->u.super_endpoint.wBytesPerInterval_hi =
usb_hi(ep.wBytesPerInterval);
}
if (ep.extra) {
memcpy(dest + bLength + superlen, ep.extra, extralen);
}
return bLength + extralen + superlen;
}
int usb_desc_other(const USBDescOther& desc, uint8_t *dest, size_t len)
{
int bLength = desc.length ? desc.length : desc.data[0];
if (len < bLength) {
return -1;
}
memcpy(dest, desc.data, bLength);
return bLength;
}
static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x07;
USBDescriptor *d = (USBDescriptor *)dest;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT;
d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */
d->u.cap.u.usb2_ext.bmAttributes_2 = 0;
d->u.cap.u.usb2_ext.bmAttributes_3 = 0;
d->u.cap.u.usb2_ext.bmAttributes_4 = 0;
return bLength;
}
static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x0a;
USBDescriptor *d = (USBDescriptor *)dest;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED;
d->u.cap.u.super.bmAttributes = 0;
d->u.cap.u.super.wSpeedsSupported_lo = 0;
d->u.cap.u.super.wSpeedsSupported_hi = 0;
d->u.cap.u.super.bFunctionalitySupport = 0;
d->u.cap.u.super.bU1DevExitLat = 0x0a;
d->u.cap.u.super.wU2DevExitLat_lo = 0x20;
d->u.cap.u.super.wU2DevExitLat_hi = 0;
if (desc->full) {
d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1);
d->u.cap.u.super.bFunctionalitySupport = 1;
}
if (desc->high) {
d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2);
if (!d->u.cap.u.super.bFunctionalitySupport) {
d->u.cap.u.super.bFunctionalitySupport = 2;
}
}
if (desc->super) {
d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3);
if (!d->u.cap.u.super.bFunctionalitySupport) {
d->u.cap.u.super.bFunctionalitySupport = 3;
}
}
return bLength;
}
static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x05;
uint16_t wTotalLength = 0;
uint8_t bNumDeviceCaps = 0;
USBDescriptor *d = (USBDescriptor *)dest;
int rc;
if (len < bLength) {
return -1;
}
d->bLength = bLength;
d->bDescriptorType = USB_DT_BOS;
wTotalLength += bLength;
if (desc->high != NULL) {
rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength,
len - wTotalLength);
if (rc < 0) {
return rc;
}
wTotalLength += rc;
bNumDeviceCaps++;
}
if (desc->super != NULL) {
rc = usb_desc_cap_super(desc, dest + wTotalLength,
len - wTotalLength);
if (rc < 0) {
return rc;
}
wTotalLength += rc;
bNumDeviceCaps++;
}
d->u.bos.wTotalLength_lo = usb_lo(wTotalLength);
d->u.bos.wTotalLength_hi = usb_hi(wTotalLength);
d->u.bos.bNumDeviceCaps = bNumDeviceCaps;
return wTotalLength;
}
int usb_desc_parse_dev (const uint8_t *data, int len, USBDesc& desc, USBDescDevice& dev)
{
USBDescriptor *d = (USBDescriptor *)data;
if (d->bLength != len || d->bDescriptorType != USB_DT_DEVICE)
return -1;
dev.bcdUSB = d->u.device.bcdUSB_lo | (d->u.device.bcdUSB_hi << 8);
dev.bDeviceClass = d->u.device.bDeviceClass;
dev.bDeviceSubClass = d->u.device.bDeviceSubClass;
dev.bDeviceProtocol = d->u.device.bDeviceProtocol;
dev.bMaxPacketSize0 = d->u.device.bMaxPacketSize0;
desc.id.idVendor = d->u.device.idVendor_lo | (d->u.device.idVendor_hi << 8);
desc.id.idProduct = d->u.device.idProduct_lo | (d->u.device.idProduct_hi << 8);
desc.id.bcdDevice = d->u.device.bcdDevice_lo | (d->u.device.bcdDevice_hi << 8);
desc.id.iManufacturer = d->u.device.iManufacturer;
desc.id.iProduct = d->u.device.iProduct;
desc.id.iSerialNumber = d->u.device.iSerialNumber;
dev.bNumConfigurations = d->u.device.bNumConfigurations;
return d->bLength;
}
int usb_desc_parse_config (const uint8_t *data, int len, USBDescDevice& dev)
{
int pos = 0;
USBDescIface *iface = nullptr;
USBDescIfaceAssoc *ifaceAssoc = nullptr;
USBDescConfig *config = nullptr;
USBDescriptor *d;
while (pos < len)
{
d = (USBDescriptor *)(data + pos);
switch (d->bDescriptorType)
{
case USB_DT_CONFIG:
{
dev.confs.push_back ({});
config = &dev.confs.back();
config->bNumInterfaces = d->u.config.bNumInterfaces;
config->bConfigurationValue = d->u.config.bConfigurationValue;
config->iConfiguration = d->u.config.iConfiguration;
config->bmAttributes = d->u.config.bmAttributes;
config->bMaxPower = d->u.config.bMaxPower;
}
break;
case USB_DT_INTERFACE:
{
if (!config)
return -1;
config->ifs.push_back ({});
iface = &config->ifs.back();
iface->bInterfaceNumber = d->u.intf.bInterfaceNumber;
iface->bAlternateSetting = d->u.intf.bAlternateSetting;
iface->bNumEndpoints = d->u.intf.bNumEndpoints;
iface->bInterfaceClass = d->u.intf.bInterfaceClass;
iface->bInterfaceSubClass = d->u.intf.bInterfaceSubClass;
iface->bInterfaceProtocol = d->u.intf.bInterfaceProtocol;
iface->iInterface = d->u.intf.iInterface;
}
break;
case USB_DT_ENDPOINT:
{
if (!iface)
return -1;
USBDescEndpoint ep = {};
ep.bEndpointAddress = d->u.endpoint.bEndpointAddress;
ep.bmAttributes = d->u.endpoint.bmAttributes;
ep.wMaxPacketSize = d->u.endpoint.wMaxPacketSize_lo |
(d->u.endpoint.wMaxPacketSize_hi << 8);
ep.bInterval = d->u.endpoint.bInterval;
ep.is_audio = d->bLength == 0x9; /* has bRefresh + bSynchAddress */
if (ep.is_audio)
{
ep.bRefresh = d->u.endpoint.bRefresh;
ep.bSynchAddress = d->u.endpoint.bSynchAddress;
ep.extra = data + pos + d->bLength;
}
iface->eps.push_back (ep);
}
break;
case USB_DT_INTERFACE_ASSOC:
return -1; //TODO
break;
case USB_DT_HID:
case USB_DT_CS_INTERFACE:
case USB_DT_CS_ENDPOINT:
{
if (!iface)
return -1;
iface->descs.push_back ({d->bLength, data + pos});
}
break;
case 0x00: // terminator, if any
case USB_DT_OTHER_SPEED_CONFIG:
case USB_DT_DEBUG:
break;
default:
return -1;
}
pos += d->bLength;
if (!d->bLength)
break;
}
return pos;
}
// simple `dev = {};` seems to be enough
/*void usb_desc_clear_device (USBDescDevice& dev)
{
for (auto& conf : dev.confs)
{
for (auto& ifassoc : conf.if_groups)
{
ifassoc.ifs.clear();
ifassoc = {};
}
for (auto& iface : conf.ifs)
{
iface.descs.clear();
iface.eps.clear();
iface = {};
}
}
dev = {};
}*/
/* ------------------------------------------------------------------ */
static void usb_desc_ep_init(USBDevice *dev)
{
const USBDescIface *iface;
int i, e, pid, ep;
usb_ep_init(dev);
for (i = 0; i < dev->ninterfaces; i++) {
iface = dev->ifaces[i];
if (iface == NULL) {
continue;
}
for (e = 0; e < iface->bNumEndpoints; e++) {
pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
USB_TOKEN_IN : USB_TOKEN_OUT;
ep = iface->eps[e].bEndpointAddress & 0x0f;
usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
usb_ep_set_max_packet_size(dev, pid, ep,
iface->eps[e].wMaxPacketSize);
usb_ep_set_max_streams(dev, pid, ep,
iface->eps[e].bmAttributes_super);
}
}
}
static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
int nif, int alt)
{
if (!dev->config) {
return NULL;
}
for (auto& g : dev->config->if_groups) {
for (auto& iface : g.ifs) {
if (iface.bInterfaceNumber == nif &&
iface.bAlternateSetting == alt) {
return &iface;
}
}
}
for (auto& iface : dev->config->ifs) {
if (iface.bInterfaceNumber == nif &&
iface.bAlternateSetting == alt) {
return &iface;
}
}
return NULL;
}
//static
int usb_desc_set_interface(USBDevice *dev, int index, int value)
{
const USBDescIface *iface;
int old;
iface = usb_desc_find_interface(dev, index, value);
if (iface == NULL) {
return -1;
}
old = dev->altsetting[index];
dev->altsetting[index] = value;
dev->ifaces[index] = iface;
usb_desc_ep_init(dev);
if (old != value) {
usb_device_set_interface(dev, index, old, value);
}
return 0;
}
//static
int usb_desc_set_config(USBDevice *dev, int value)
{
int i;
if (value == 0) {
dev->configuration = 0;
dev->ninterfaces = 0;
dev->config = NULL;
} else {
for (auto& i : dev->device->confs) {
if (i.bConfigurationValue == value) {
dev->configuration = value;
dev->ninterfaces = i.bNumInterfaces;
dev->config = &i;
assert(dev->ninterfaces <= USB_MAX_INTERFACES);
}
}
/*if (i < dev->device->bNumConfigurations) {
return -1;
}*/
}
for (i = 0; i < dev->ninterfaces; i++) {
usb_desc_set_interface(dev, i, 0);
}
for (; i < USB_MAX_INTERFACES; i++) {
dev->altsetting[i] = 0;
dev->ifaces[i] = NULL;
}
return 0;
}
static void usb_desc_setdefaults(USBDevice *dev)
{
const USBDesc *desc = usb_device_get_usb_desc(dev);
assert(desc != NULL);
switch (dev->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
dev->device = desc->full;
break;
/*case USB_SPEED_HIGH:
dev->device = desc->high;
break;
case USB_SPEED_SUPER:
dev->device = desc->super;
break;*/
default:
assert(false && "Unsupported speed");
}
usb_desc_set_config(dev, 0);
}
void usb_desc_init(USBDevice *dev)
{
const USBDesc *desc = usb_device_get_usb_desc(dev);
assert(desc != NULL);
dev->speed = USB_SPEED_FULL;
dev->speedmask = 0;
if (desc->full) {
dev->speedmask |= USB_SPEED_MASK_FULL;
}
usb_desc_setdefaults(dev);
}
void usb_desc_attach(USBDevice *dev)
{
usb_desc_setdefaults(dev);
}
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
{
uint8_t bLength, pos, i;
const char *str;
if (len < 4) {
return -1;
}
if (index == 0) {
/* language ids */
dest[0] = 4;
dest[1] = USB_DT_STRING;
dest[2] = 0x09;
dest[3] = 0x04;
return 4;
}
str = usb_device_get_usb_desc(dev)->str[index];
if (str == NULL) {
return 0;
}
bLength = strlen(str) * 2 + 2;
dest[0] = bLength;
dest[1] = USB_DT_STRING;
i = 0; pos = 2;
while (pos+1 < bLength && pos+1 < len) {
dest[pos++] = str[i++];
dest[pos++] = 0;
}
return pos;
}
int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
int value, uint8_t *dest, size_t len)
{
bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
const USBDesc *desc = usb_device_get_usb_desc(dev);
const USBDescDevice *other_dev;
uint8_t buf[1024];
uint8_t type = value >> 8;
uint8_t index = value & 0xff;
int flags, ret = -1;
if (dev->speed == USB_SPEED_HIGH) {
other_dev = usb_device_get_usb_desc(dev)->full;
} else {
other_dev = usb_device_get_usb_desc(dev)->high;
}
flags = 0;
if (dev->device->bcdUSB >= 0x0300) {
flags |= USB_DESC_FLAG_SUPER;
}
switch(type) {
case USB_DT_DEVICE:
ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf));
//trace_usb_desc_device(dev->addr, len, ret);
break;
case USB_DT_CONFIG:
if (index < dev->device->bNumConfigurations) {
ret = usb_desc_config(dev->device->confs[index], flags,
buf, sizeof(buf));
}
//trace_usb_desc_config(dev->addr, index, len, ret);
break;
case USB_DT_STRING:
memset(buf, 0, sizeof(buf));
ret = usb_desc_string(dev, index, buf, sizeof(buf));
//trace_usb_desc_string(dev->addr, index, len, ret);
break;
case USB_DT_DEVICE_QUALIFIER:
if (other_dev != NULL) {
ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
}
//trace_usb_desc_device_qualifier(dev->addr, len, ret);
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (other_dev != NULL && index < other_dev->bNumConfigurations) {
ret = usb_desc_config(other_dev->confs[index], flags,
buf, sizeof(buf));
buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
}
//trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
break;
case USB_DT_BOS:
ret = usb_desc_bos(desc, buf, sizeof(buf));
//trace_usb_desc_bos(dev->addr, len, ret);
break;
case USB_DT_DEBUG:
/* ignore silently */
break;
default:
OSDebugOut(TEXT("%s: %d unknown type %d (len %zd)\n"), __func__,
dev->addr, type, len);
break;
}
if (ret > 0) {
if (ret > len) {
ret = len;
}
memcpy(dest, buf, ret);
p->actual_length = ret;
ret = 0;
}
return ret;
}
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
const USBDesc *desc = usb_device_get_usb_desc(dev);
int ret = -1;
assert(desc != NULL);
switch(request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
dev->addr = value;
//trace_usb_set_addr(dev->addr);
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
ret = usb_desc_get_descriptor(dev, p, value, data, length);
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
/*
* 9.4.2: 0 should be returned if the device is unconfigured, otherwise
* the non zero value of bConfigurationValue.
*/
data[0] = dev->config ? dev->config->bConfigurationValue : 0;
p->actual_length = 1;
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
ret = usb_desc_set_config(dev, value);
//trace_usb_set_config(dev->addr, value, ret);
break;
case DeviceRequest | USB_REQ_GET_STATUS: {
const USBDescConfig *config = dev->config ?
dev->config : &dev->device->confs[0];
data[0] = 0;
/*
* Default state: Device behavior when this request is received while
* the device is in the Default state is not specified.
* We return the same value that a configured device would return if
* it used the first configuration.
*/
if (config->bmAttributes & USB_CFG_ATT_SELFPOWER) {
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
}
if (dev->remote_wakeup) {
data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
}
data[1] = 0x00;
p->actual_length = 2;
ret = 0;
break;
}
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 0;
ret = 0;
}
//trace_usb_clear_device_feature(dev->addr, value, ret);
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 1;
ret = 0;
}
//trace_usb_set_device_feature(dev->addr, value, ret);
break;
/* case DeviceOutRequest | USB_REQ_SET_SEL:
case DeviceOutRequest | USB_REQ_SET_ISOCH_DELAY:
if (dev->speed == USB_SPEED_SUPER) {
ret = 0;
}
break;
*/
case InterfaceRequest | USB_REQ_GET_INTERFACE:
if (index < 0 || index >= dev->ninterfaces) {
break;
}
data[0] = dev->altsetting[index];
p->actual_length = 1;
ret = 0;
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
OSDebugOut (TEXT("usb_desc_set_interface\n"));
ret = usb_desc_set_interface(dev, index, value);
//trace_usb_set_interface(dev->addr, index, value, ret);
break;
}
return ret;
}

247
pcsx2/USB/qemu-usb/desc.h Normal file
View File

@ -0,0 +1,247 @@
#ifndef QEMU_HW_USB_DESC_H
#define QEMU_HW_USB_DESC_H
#include "platcompat.h"
#include <wchar.h>
#include <vector>
/* binary representation */
PACK(typedef struct USBDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
union {
struct {
uint8_t bcdUSB_lo;
uint8_t bcdUSB_hi;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint8_t idVendor_lo;
uint8_t idVendor_hi;
uint8_t idProduct_lo;
uint8_t idProduct_hi;
uint8_t bcdDevice_lo;
uint8_t bcdDevice_hi;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} device;
struct {
uint8_t bcdUSB_lo;
uint8_t bcdUSB_hi;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint8_t bNumConfigurations;
uint8_t bReserved;
} device_qualifier;
struct {
uint8_t wTotalLength_lo;
uint8_t wTotalLength_hi;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} config;
struct {
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} intf;
struct {
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint8_t wMaxPacketSize_lo;
uint8_t wMaxPacketSize_hi;
uint8_t bInterval;
uint8_t bRefresh; /* only audio ep */
uint8_t bSynchAddress; /* only audio ep */
} endpoint;
struct {
uint8_t bMaxBurst;
uint8_t bmAttributes;
uint8_t wBytesPerInterval_lo;
uint8_t wBytesPerInterval_hi;
} super_endpoint;
struct {
uint8_t wTotalLength_lo;
uint8_t wTotalLength_hi;
uint8_t bNumDeviceCaps;
} bos;
struct {
uint8_t bDevCapabilityType;
union {
struct {
uint8_t bmAttributes_1;
uint8_t bmAttributes_2;
uint8_t bmAttributes_3;
uint8_t bmAttributes_4;
} usb2_ext;
struct {
uint8_t bmAttributes;
uint8_t wSpeedsSupported_lo;
uint8_t wSpeedsSupported_hi;
uint8_t bFunctionalitySupport;
uint8_t bU1DevExitLat;
uint8_t wU2DevExitLat_lo;
uint8_t wU2DevExitLat_hi;
} super;
} u;
} cap;
} u;
}, USBDescriptor);
struct USBDescID {
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
};
struct USBDescDevice {
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint8_t bNumConfigurations;
std::vector<USBDescConfig> confs;
};
struct USBDescConfig {
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
/* grouped interfaces */
//uint8_t nif_groups;
std::vector<USBDescIfaceAssoc> if_groups;
/* "normal" interfaces */
//uint8_t nif;
std::vector<USBDescIface> ifs;
};
/* conceptually an Interface Association Descriptor, and releated interfaces */
struct USBDescIfaceAssoc {
uint8_t bFirstInterface;
uint8_t bInterfaceCount;
uint8_t bFunctionClass;
uint8_t bFunctionSubClass;
uint8_t bFunctionProtocol;
uint8_t iFunction;
//uint8_t nif;
std::vector<USBDescIface> ifs;
};
struct USBDescIface {
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
//uint8_t ndesc;
std::vector<USBDescOther> descs;
std::vector<USBDescEndpoint> eps;
};
struct USBDescEndpoint {
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
uint8_t bRefresh;
uint8_t bSynchAddress;
uint8_t is_audio; /* has bRefresh + bSynchAddress */
const uint8_t *extra;
/* superspeed endpoint companion */
uint8_t bMaxBurst;
uint8_t bmAttributes_super;
uint16_t wBytesPerInterval;
};
struct USBDescOther {
uint8_t length;
const uint8_t *data;
};
struct USBDescMSOS {
const char *CompatibleID;
const wchar_t *Label;
bool SelectiveSuspendEnabled;
};
typedef const char *USBDescStrings[256];
struct USBDesc {
USBDescID id;
const USBDescDevice *full;
const USBDescDevice *high;
const USBDescDevice *super;
const char* const *str;
const USBDescMSOS *msos;
};
#define USB_DESC_FLAG_SUPER (1 << 1)
/* little helpers */
static inline uint8_t usb_lo(uint16_t val)
{
return val & 0xff;
}
static inline uint8_t usb_hi(uint16_t val)
{
return (val >> 8) & 0xff;
}
/* generate usb packages from structs */
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
bool msos, uint8_t *dest, size_t len);
int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len);
int usb_desc_config(const USBDescConfig& conf, int flags,
uint8_t *dest, size_t len);
int usb_desc_iface_group(const USBDescIfaceAssoc& iad, int flags,
uint8_t *dest, size_t len);
int usb_desc_iface(const USBDescIface& iface, int flags,
uint8_t *dest, size_t len);
int usb_desc_endpoint(const USBDescEndpoint& ep, int flags,
uint8_t *dest, size_t len);
int usb_desc_other(const USBDescOther& desc, uint8_t *dest, size_t len);
//int usb_desc_msos(const USBDesc *desc, USBPacket *p,
// int index, uint8_t *dest, size_t len);
int usb_desc_parse_dev (const uint8_t *data, int len, USBDesc& desc, USBDescDevice& dev);
int usb_desc_parse_config (const uint8_t *data, int len, USBDescDevice& dev);
/* control message emulation helpers */
void usb_desc_init(USBDevice *dev);
void usb_desc_attach(USBDevice *dev);
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
int value, uint8_t *dest, size_t len);
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data);
int usb_desc_set_config(USBDevice *dev, int value);
int usb_desc_set_interface(USBDevice *dev, int index, int value);
#endif /* QEMU_HW_USB_DESC_H */

151
pcsx2/USB/qemu-usb/glib.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "glib.h"
#include <cstdlib>
#include <cstring>
#define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b)))
/**
* g_malloc:
* @n_bytes: the number of bytes to allocate
*
* Allocates @n_bytes bytes of memory.
* If @n_bytes is 0 it returns %NULL.
*
* Returns: a pointer to the allocated memory
*/
void*
my_g_malloc (size_t n_bytes)
{
if (G_LIKELY (n_bytes))
{
void* mem;
mem = malloc (n_bytes);
//TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0));
if (mem)
return mem;
//g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
// G_STRLOC, n_bytes);
}
//TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0));
return NULL;
}
/**
* g_malloc0:
* @n_bytes: the number of bytes to allocate
*
* Allocates @n_bytes bytes of memory, initialized to 0's.
* If @n_bytes is 0 it returns %NULL.
*
* Returns: a pointer to the allocated memory
*/
void*
my_g_malloc0 (size_t n_bytes)
{
if (G_LIKELY (n_bytes))
{
void* mem;
mem = calloc (1, n_bytes);
//TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0));
if (mem)
return mem;
//g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
// G_STRLOC, n_bytes);
}
//TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0));
return NULL;
}
/**
* g_malloc_n:
* @n_blocks: the number of blocks to allocate
* @n_block_bytes: the size of each block in bytes
*
* This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes,
* but care is taken to detect possible overflow during multiplication.
*
* Since: 2.24
* Returns: a pointer to the allocated memory
*/
void*
my_g_malloc_n (size_t n_blocks,
size_t n_block_bytes)
{
if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
{
//g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
// G_STRLOC, n_blocks, n_block_bytes);
}
return my_g_malloc (n_blocks * n_block_bytes);
}
/**
* g_realloc:
* @mem: (nullable): the memory to reallocate
* @n_bytes: new size of the memory in bytes
*
* Reallocates the memory pointed to by @mem, so that it now has space for
* @n_bytes bytes of memory. It returns the new address of the memory, which may
* have been moved. @mem may be %NULL, in which case it's considered to
* have zero-length. @n_bytes may be 0, in which case %NULL will be returned
* and @mem will be freed unless it is %NULL.
*
* Returns: the new address of the allocated memory
*/
void*
my_g_realloc (void* mem,
size_t n_bytes)
{
void* newmem;
if (G_LIKELY (n_bytes))
{
newmem = realloc (mem, n_bytes);
//TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0));
if (newmem)
return newmem;
//g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
// G_STRLOC, n_bytes);
}
if (mem)
free (mem);
//TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0));
return NULL;
}
/**
* g_realloc_n:
* @mem: (nullable): the memory to reallocate
* @n_blocks: the number of blocks to allocate
* @n_block_bytes: the size of each block in bytes
*
* This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes,
* but care is taken to detect possible overflow during multiplication.
*
* Since: 2.24
* Returns: the new address of the allocated memory
*/
void*
my_g_realloc_n (void* mem,
size_t n_blocks,
size_t n_block_bytes)
{
if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
{
//g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
// G_STRLOC, n_blocks, n_block_bytes);
}
return my_g_realloc (mem, n_blocks * n_block_bytes);
}

31
pcsx2/USB/qemu-usb/glib.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef GLIB_H
#define GLIB_H
#include <cstddef>
#include <cstdint>
#include "gmem-size.h"
#define G_MAXUINT64 0xffffffffffffffffUL
#define G_MAXUINT32 ((uint32_t)0xffffffff)
void* my_g_malloc0 (size_t n_bytes);
void* my_g_malloc (size_t n_bytes);
void* my_g_malloc_n (size_t n_blocks,
size_t n_block_bytes);
void* my_g_realloc_n (void* mem,
size_t n_blocks,
size_t n_block_bytes);
#define my_g_free free
#define G_LIKELY(expr) (expr)
#define G_UNLIKELY(expr) (expr)
#define my_G_NEW(struct_type, n_structs, func) \
((struct_type *) my_g_##func##_n ((n_structs), sizeof (struct_type)))
#define my_G_RENEW(struct_type, mem, n_structs, func) \
((struct_type *) my_g_##func##_n (mem, (n_structs), sizeof (struct_type)))
#define my_g_new(struct_type, n_structs) my_G_NEW (struct_type, n_structs, malloc)
#define my_g_renew(struct_type, mem, n_structs) my_G_RENEW (struct_type, mem, n_structs, realloc)
#endif

544
pcsx2/USB/qemu-usb/hid.cpp Normal file
View File

@ -0,0 +1,544 @@
/*
* QEMU HID devices
*
* Copyright (c) 2005 Fabrice Bellard
* Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com)
*
* 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 "hid.h"
#include "input-keymap.h"
#include "osdebugout.h"
#define HID_USAGE_ERROR_ROLLOVER 0x01
#define HID_USAGE_POSTFAIL 0x02
#define HID_USAGE_ERROR_UNDEFINED 0x03
/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
* above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
static const uint8_t hid_usage_keys[0x100] = {
0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
0x5a, 0x5b, 0x62, 0x63, 0x46, 0x00, 0x64, 0x44,
0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x85, 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, 0x58, 0xe4, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x48, 0x4a,
0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x66, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
bool hid_has_events(HIDState *hs)
{
return hs->n > 0 || hs->idle_pending;
}
static void hid_idle_timer(void *opaque)
{
HIDState *hs = (HIDState *)opaque;
hs->idle_pending = true;
hs->event(hs);
}
static void hid_del_idle_timer(HIDState *hs)
{
/*if (hs->idle_timer) {
timer_del(hs->idle_timer);
timer_free(hs->idle_timer);
hs->idle_timer = NULL;
}*/
}
void hid_set_next_idle(HIDState *hs)
{
/*if (hs->idle) {
uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000;
if (!hs->idle_timer) {
hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
}
timer_mod_ns(hs->idle_timer, expire_time);
}
else {
hid_del_idle_timer(hs);
}*/
}
static void hid_pointer_event(HIDState *hs, InputEvent *evt)
{
static const int bmap[INPUT_BUTTON__MAX] = {
/*[INPUT_BUTTON_LEFT] =*/ 0x01,
/*[INPUT_BUTTON_MIDDLE] =*/ 0x04,
/*[INPUT_BUTTON_RIGHT] =*/ 0x02,
0,0,0,0
};
HIDPointerEvent *e;
InputMoveEvent *move;
InputBtnEvent *btn;
assert(hs->n < QUEUE_LENGTH);
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
switch (evt->type) {
case INPUT_EVENT_KIND_REL:
move = &evt->u.rel;
if (move->axis == INPUT_AXIS_X) {
e->xdx += move->value;
}
else if (move->axis == INPUT_AXIS_Y) {
e->ydy += move->value;
}
break;
case INPUT_EVENT_KIND_ABS:
move = &evt->u.abs;
if (move->axis == INPUT_AXIS_X) {
e->xdx = move->value;
}
else if (move->axis == INPUT_AXIS_Y) {
e->ydy = move->value;
}
break;
case INPUT_EVENT_KIND_BTN:
btn = &evt->u.btn;
if (btn->down) {
e->buttons_state |= bmap[btn->button];
if (btn->button == INPUT_BUTTON_WHEEL_UP) {
e->dz--;
}
else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
e->dz++;
}
}
else {
e->buttons_state &= ~bmap[btn->button];
}
break;
default:
/* keep gcc happy */
break;
}
}
static void hid_pointer_sync(HIDState *hs)
{
HIDPointerEvent *prev, *curr, *next;
bool event_compression = false;
if (hs->n == QUEUE_LENGTH - 1) {
/*
* Queue full. We are losing information, but we at least
* keep track of most recent button state.
*/
return;
}
prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
if (hs->n > 0) {
/*
* No button state change between previous and current event
* (and previous wasn't seen by the guest yet), so there is
* motion information only and we can combine the two event
* into one.
*/
if (curr->buttons_state == prev->buttons_state) {
event_compression = true;
}
}
if (event_compression) {
/* add current motion to previous, clear current */
if (hs->kind == HID_MOUSE) {
prev->xdx += curr->xdx;
curr->xdx = 0;
prev->ydy += curr->ydy;
curr->ydy = 0;
}
else {
prev->xdx = curr->xdx;
prev->ydy = curr->ydy;
}
prev->dz += curr->dz;
curr->dz = 0;
}
else {
/* prepate next (clear rel, copy abs + btns) */
if (hs->kind == HID_MOUSE) {
next->xdx = 0;
next->ydy = 0;
}
else {
next->xdx = curr->xdx;
next->ydy = curr->ydy;
}
next->dz = 0;
next->buttons_state = curr->buttons_state;
/* make current guest visible, notify guest */
hs->n++;
hs->event(hs);
}
}
static void hid_keyboard_event(HIDState *hs, InputEvent *evt)
{
int scancodes[3], i, count;
int slot;
InputKeyEvent *key = &evt->u.key;
count = qemu_input_key_value_to_scancode(&key->key,
key->down,
scancodes);
if (hs->n + count > QUEUE_LENGTH) {
//trace_hid_kbd_queue_full();
return;
}
for (i = 0; i < count; i++) {
slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
hs->kbd.keycodes[slot] = scancodes[i];
}
hs->event(hs);
}
static void hid_keyboard_process_keycode(HIDState *hs)
{
uint8_t hid_code, index, key;
int i, keycode, slot;
if (hs->n == 0) {
return;
}
slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
keycode = hs->kbd.keycodes[slot];
if (!hs->n) {
//trace_hid_kbd_queue_empty();
}
key = keycode & 0x7f;
index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
hid_code = hid_usage_keys[index];
hs->kbd.modifiers &= ~(1 << 8);
switch (hid_code) {
case 0x00:
return;
case 0xe0:
assert(key == 0x1d);
if (hs->kbd.modifiers & (1 << 9)) {
/* The hid_codes for the 0xe1/0x1d scancode sequence are 0xe9/0xe0.
* Here we're processing the second hid_code. By dropping bit 9
* and setting bit 8, the scancode after 0x1d will access the
* second half of the table.
*/
hs->kbd.modifiers ^= (1 << 8) | (1 << 9);
return;
}
/* fall through to process Ctrl_L */
//case 0xe1 ... 0xe7:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
/* Ctrl_L/Ctrl_R, Shift_L/Shift_R, Alt_L/Alt_R, Win_L/Win_R.
* Handle releases here, or fall through to process presses.
*/
if (keycode & (1 << 7)) {
hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
return;
}
/* fall through */
case 0xe8:
case 0xe9:
/* USB modifiers are just 1 byte long. Bits 8 and 9 of
* hs->kbd.modifiers implement a state machine that detects the
* 0xe0 and 0xe1/0x1d sequences. These bits do not follow the
* usual rules where bit 7 marks released keys; they are cleared
* elsewhere in the function as the state machine dictates.
*/
hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
return;
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
#ifdef _DEBUG
abort();
#endif
default:
break;
}
if (keycode & (1 << 7)) {
for (i = hs->kbd.keys - 1; i >= 0; i--) {
if (hs->kbd.key[i] == hid_code) {
hs->kbd.key[i] = hs->kbd.key[--hs->kbd.keys];
hs->kbd.key[hs->kbd.keys] = 0x00;
break;
}
}
if (i < 0) {
return;
}
}
else {
for (i = hs->kbd.keys - 1; i >= 0; i--) {
if (hs->kbd.key[i] == hid_code) {
break;
}
}
if (i < 0) {
if (hs->kbd.keys < sizeof(hs->kbd.key)) {
hs->kbd.key[hs->kbd.keys++] = hid_code;
}
}
else {
return;
}
}
}
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;
}
}
void hid_pointer_activate(HIDState *hs)
{
if (!hs->ptr.mouse_grabbed) {
//qemu_input_handler_activate(hs->s);
hs->ptr.mouse_grabbed = 1;
}
}
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
{
int dx, dy, dz, l;
int index;
HIDPointerEvent *e;
hs->idle_pending = false;
hid_pointer_activate(hs);
/* When the buffer is empty, return the last event. Relative
movements will all be zero. */
index = (hs->n ? hs->head : hs->head - 1);
e = &hs->ptr.queue[index & QUEUE_MASK];
if (hs->kind == HID_MOUSE) {
dx = int_clamp(e->xdx, -127, 127);
dy = int_clamp(e->ydy, -127, 127);
e->xdx -= dx;
e->ydy -= dy;
}
else {
dx = e->xdx;
dy = e->ydy;
}
dz = int_clamp(e->dz, -127, 127);
e->dz -= dz;
if (hs->n &&
!e->dz &&
(hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
/* that deals with this event */
QUEUE_INCR(hs->head);
hs->n--;
}
/* Appears we have to invert the wheel direction */
dz = 0 - dz;
l = 0;
switch (hs->kind) {
case HID_MOUSE:
if (len > l) {
buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx;
}
if (len > l) {
buf[l++] = dy;
}
if (len > l) {
buf[l++] = dz;
}
break;
case HID_TABLET:
if (len > l) {
buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx & 0xff;
}
if (len > l) {
buf[l++] = dx >> 8;
}
if (len > l) {
buf[l++] = dy & 0xff;
}
if (len > l) {
buf[l++] = dy >> 8;
}
if (len > l) {
buf[l++] = dz;
}
break;
default:
abort();
}
return l;
}
int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
{
hs->idle_pending = false;
if (len < 2) {
return 0;
}
hid_keyboard_process_keycode(hs);
buf[0] = hs->kbd.modifiers & 0xff;
buf[1] = 0;
if (hs->kbd.keys > 6) {
memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
}
else {
memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
}
return MIN(8, len);
}
int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
{
if (len > 0) {
int ledstate = 0;
/* 0x01: Num Lock LED
* 0x02: Caps Lock LED
* 0x04: Scroll Lock LED
* 0x08: Compose LED
* 0x10: Kana LED */
hs->kbd.leds = buf[0];
if (hs->kbd.leds & 0x04) {
ledstate |= QEMU_SCROLL_LOCK_LED;
}
if (hs->kbd.leds & 0x01) {
ledstate |= QEMU_NUM_LOCK_LED;
}
if (hs->kbd.leds & 0x02) {
ledstate |= QEMU_CAPS_LOCK_LED;
}
//kbd_put_ledstate(ledstate);
}
return 0;
}
void hid_reset(HIDState *hs)
{
switch (hs->kind) {
case HID_KEYBOARD:
memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
hs->kbd.keys = 0;
hs->kbd.modifiers = 0;
break;
case HID_MOUSE:
case HID_TABLET:
memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
break;
}
hs->head = 0;
hs->n = 0;
hs->protocol = 1;
hs->idle = 0;
hs->idle_pending = false;
hid_del_idle_timer(hs);
}
void hid_free(HIDState *hs)
{
//qemu_input_handler_unregister(hs->s);
hid_del_idle_timer(hs);
}
void hid_init(HIDState *hs, int kind, HIDEventFunc event)
{
hs->kind = kind;
hs->event = event;
if (hs->kind == HID_KEYBOARD) {
hs->kbd.eh_entry = hid_keyboard_event;
} else if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
hs->ptr.eh_entry = hid_pointer_event;
hs->ptr.eh_sync = hid_pointer_sync;
}
}

312
pcsx2/USB/qemu-usb/hid.h Normal file
View File

@ -0,0 +1,312 @@
#ifndef QEMU_HID_H
#define QEMU_HID_H
#include "vl.h"
/* include/ui/console.h */
/* keyboard/mouse support */
#define MOUSE_EVENT_LBUTTON 0x01
#define MOUSE_EVENT_RBUTTON 0x02
#define MOUSE_EVENT_MBUTTON 0x04
/* identical to the ps/2 keyboard bits */
#define QEMU_SCROLL_LOCK_LED (1 << 0)
#define QEMU_NUM_LOCK_LED (1 << 1)
#define QEMU_CAPS_LOCK_LED (1 << 2)
#define HID_MOUSE 1
#define HID_TABLET 2
#define HID_KEYBOARD 3
/* scancode without modifiers */
#define SCANCODE_KEYMASK 0xff
/* scancode without grey or up bit */
#define SCANCODE_KEYCODEMASK 0x7f
/* "grey" keys will usually need a 0xe0 prefix */
#define SCANCODE_GREY 0x80
#define SCANCODE_EMUL0 0xE0
#define SCANCODE_EMUL1 0xE1
/* "up" flag */
#define SCANCODE_UP 0x80
/* Additional modifiers to use if not catched another way. */
#define SCANCODE_SHIFT 0x100
#define SCANCODE_CTRL 0x200
#define SCANCODE_ALT 0x400
#define SCANCODE_ALTGR 0x800
typedef enum QKeyCode {
Q_KEY_CODE_UNMAPPED = 0,
Q_KEY_CODE_SHIFT = 1,
Q_KEY_CODE_SHIFT_R = 2,
Q_KEY_CODE_ALT = 3,
Q_KEY_CODE_ALT_R = 4,
Q_KEY_CODE_CTRL = 5,
Q_KEY_CODE_CTRL_R = 6,
Q_KEY_CODE_MENU = 7,
Q_KEY_CODE_ESC = 8,
Q_KEY_CODE_1 = 9,
Q_KEY_CODE_2 = 10,
Q_KEY_CODE_3 = 11,
Q_KEY_CODE_4 = 12,
Q_KEY_CODE_5 = 13,
Q_KEY_CODE_6 = 14,
Q_KEY_CODE_7 = 15,
Q_KEY_CODE_8 = 16,
Q_KEY_CODE_9 = 17,
Q_KEY_CODE_0 = 18,
Q_KEY_CODE_MINUS = 19,
Q_KEY_CODE_EQUAL = 20,
Q_KEY_CODE_BACKSPACE = 21,
Q_KEY_CODE_TAB = 22,
Q_KEY_CODE_Q = 23,
Q_KEY_CODE_W = 24,
Q_KEY_CODE_E = 25,
Q_KEY_CODE_R = 26,
Q_KEY_CODE_T = 27,
Q_KEY_CODE_Y = 28,
Q_KEY_CODE_U = 29,
Q_KEY_CODE_I = 30,
Q_KEY_CODE_O = 31,
Q_KEY_CODE_P = 32,
Q_KEY_CODE_BRACKET_LEFT = 33,
Q_KEY_CODE_BRACKET_RIGHT = 34,
Q_KEY_CODE_RET = 35,
Q_KEY_CODE_A = 36,
Q_KEY_CODE_S = 37,
Q_KEY_CODE_D = 38,
Q_KEY_CODE_F = 39,
Q_KEY_CODE_G = 40,
Q_KEY_CODE_H = 41,
Q_KEY_CODE_J = 42,
Q_KEY_CODE_K = 43,
Q_KEY_CODE_L = 44,
Q_KEY_CODE_SEMICOLON = 45,
Q_KEY_CODE_APOSTROPHE = 46,
Q_KEY_CODE_GRAVE_ACCENT = 47,
Q_KEY_CODE_BACKSLASH = 48,
Q_KEY_CODE_Z = 49,
Q_KEY_CODE_X = 50,
Q_KEY_CODE_C = 51,
Q_KEY_CODE_V = 52,
Q_KEY_CODE_B = 53,
Q_KEY_CODE_N = 54,
Q_KEY_CODE_M = 55,
Q_KEY_CODE_COMMA = 56,
Q_KEY_CODE_DOT = 57,
Q_KEY_CODE_SLASH = 58,
Q_KEY_CODE_ASTERISK = 59,
Q_KEY_CODE_SPC = 60,
Q_KEY_CODE_CAPS_LOCK = 61,
Q_KEY_CODE_F1 = 62,
Q_KEY_CODE_F2 = 63,
Q_KEY_CODE_F3 = 64,
Q_KEY_CODE_F4 = 65,
Q_KEY_CODE_F5 = 66,
Q_KEY_CODE_F6 = 67,
Q_KEY_CODE_F7 = 68,
Q_KEY_CODE_F8 = 69,
Q_KEY_CODE_F9 = 70,
Q_KEY_CODE_F10 = 71,
Q_KEY_CODE_NUM_LOCK = 72,
Q_KEY_CODE_SCROLL_LOCK = 73,
Q_KEY_CODE_KP_DIVIDE = 74,
Q_KEY_CODE_KP_MULTIPLY = 75,
Q_KEY_CODE_KP_SUBTRACT = 76,
Q_KEY_CODE_KP_ADD = 77,
Q_KEY_CODE_KP_ENTER = 78,
Q_KEY_CODE_KP_DECIMAL = 79,
Q_KEY_CODE_SYSRQ = 80,
Q_KEY_CODE_KP_0 = 81,
Q_KEY_CODE_KP_1 = 82,
Q_KEY_CODE_KP_2 = 83,
Q_KEY_CODE_KP_3 = 84,
Q_KEY_CODE_KP_4 = 85,
Q_KEY_CODE_KP_5 = 86,
Q_KEY_CODE_KP_6 = 87,
Q_KEY_CODE_KP_7 = 88,
Q_KEY_CODE_KP_8 = 89,
Q_KEY_CODE_KP_9 = 90,
Q_KEY_CODE_LESS = 91,
Q_KEY_CODE_F11 = 92,
Q_KEY_CODE_F12 = 93,
Q_KEY_CODE_PRINT = 94,
Q_KEY_CODE_HOME = 95,
Q_KEY_CODE_PGUP = 96,
Q_KEY_CODE_PGDN = 97,
Q_KEY_CODE_END = 98,
Q_KEY_CODE_LEFT = 99,
Q_KEY_CODE_UP = 100,
Q_KEY_CODE_DOWN = 101,
Q_KEY_CODE_RIGHT = 102,
Q_KEY_CODE_INSERT = 103,
Q_KEY_CODE_DELETE = 104,
Q_KEY_CODE_STOP = 105,
Q_KEY_CODE_AGAIN = 106,
Q_KEY_CODE_PROPS = 107,
Q_KEY_CODE_UNDO = 108,
Q_KEY_CODE_FRONT = 109,
Q_KEY_CODE_COPY = 110,
Q_KEY_CODE_OPEN = 111,
Q_KEY_CODE_PASTE = 112,
Q_KEY_CODE_FIND = 113,
Q_KEY_CODE_CUT = 114,
Q_KEY_CODE_LF = 115,
Q_KEY_CODE_HELP = 116,
Q_KEY_CODE_META_L = 117,
Q_KEY_CODE_META_R = 118,
Q_KEY_CODE_COMPOSE = 119,
Q_KEY_CODE_PAUSE = 120,
Q_KEY_CODE_RO = 121,
Q_KEY_CODE_HIRAGANA = 122,
Q_KEY_CODE_HENKAN = 123,
Q_KEY_CODE_YEN = 124,
Q_KEY_CODE_MUHENKAN = 125,
Q_KEY_CODE_KATAKANAHIRAGANA = 126,
Q_KEY_CODE_KP_COMMA = 127,
Q_KEY_CODE_KP_EQUALS = 128,
Q_KEY_CODE_POWER = 129,
Q_KEY_CODE_SLEEP = 130,
Q_KEY_CODE_WAKE = 131,
Q_KEY_CODE_AUDIONEXT = 132,
Q_KEY_CODE_AUDIOPREV = 133,
Q_KEY_CODE_AUDIOSTOP = 134,
Q_KEY_CODE_AUDIOPLAY = 135,
Q_KEY_CODE_AUDIOMUTE = 136,
Q_KEY_CODE_VOLUMEUP = 137,
Q_KEY_CODE_VOLUMEDOWN = 138,
Q_KEY_CODE_MEDIASELECT = 139,
Q_KEY_CODE_MAIL = 140,
Q_KEY_CODE_CALCULATOR = 141,
Q_KEY_CODE_COMPUTER = 142,
Q_KEY_CODE_AC_HOME = 143,
Q_KEY_CODE_AC_BACK = 144,
Q_KEY_CODE_AC_FORWARD = 145,
Q_KEY_CODE_AC_REFRESH = 146,
Q_KEY_CODE_AC_BOOKMARKS = 147,
Q_KEY_CODE__MAX = 148,
} QKeyCode;
typedef enum InputEventKind {
INPUT_EVENT_KIND_KEY = 0,
INPUT_EVENT_KIND_BTN = 1,
INPUT_EVENT_KIND_REL = 2,
INPUT_EVENT_KIND_ABS = 3,
INPUT_EVENT_KIND__MAX = 4,
} InputEventKind;
typedef enum KeyValueKind {
KEY_VALUE_KIND_NUMBER = 0,
KEY_VALUE_KIND_QCODE = 1,
KEY_VALUE_KIND__MAX = 2,
} KeyValueKind;
typedef enum InputAxis {
INPUT_AXIS_X = 0,
INPUT_AXIS_Y = 1,
INPUT_AXIS__MAX = 2,
} InputAxis;
typedef enum InputButton {
INPUT_BUTTON_LEFT = 0,
INPUT_BUTTON_MIDDLE = 1,
INPUT_BUTTON_RIGHT = 2,
INPUT_BUTTON_WHEEL_UP = 3,
INPUT_BUTTON_WHEEL_DOWN = 4,
INPUT_BUTTON_SIDE = 5,
INPUT_BUTTON_EXTRA = 6,
INPUT_BUTTON__MAX = 7,
} InputButton;
struct KeyValue {
KeyValueKind type;
union {
int number;
QKeyCode qcode;
} u;
};
struct InputKeyEvent {
KeyValue key;
bool down;
};
struct InputBtnEvent {
InputButton button;
bool down;
};
struct InputMoveEvent {
InputAxis axis;
int64_t value;
};
struct InputEvent {
InputEventKind type;
union {
InputKeyEvent key;
InputBtnEvent btn;
InputMoveEvent rel;
InputMoveEvent abs;
} u;
};
typedef struct HIDState HIDState;
typedef void(*HIDEventFunc)(HIDState *s);
typedef void QEMUPutKBDEvent(HIDState *hs, InputEvent *evt);
typedef void QEMUPutLEDEvent(void *opaque, int ledstate);
typedef void QEMUPutMouseEvent(HIDState *hs, InputEvent *evt);
typedef struct HIDPointerEvent {
int32_t xdx, ydy; /* relative if it's a mouse, otherwise absolute */
int32_t dz, buttons_state;
} HIDPointerEvent;
#define QUEUE_LENGTH 16 /* should be enough for a triple-click */
#define QUEUE_MASK (QUEUE_LENGTH-1u)
#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK)
typedef struct HIDMouseState {
HIDPointerEvent queue[QUEUE_LENGTH];
int mouse_grabbed;
QEMUPutMouseEvent *eh_entry;
HIDEventFunc eh_sync;
} HIDMouseState;
typedef struct HIDKeyboardState {
uint32_t keycodes[QUEUE_LENGTH];
uint16_t modifiers;
uint8_t leds;
uint8_t key[16];
int32_t keys;
QEMUPutKBDEvent *eh_entry;
} HIDKeyboardState;
struct HIDState {
union {
HIDMouseState ptr;
HIDKeyboardState kbd;
};
uint32_t head; /* index into circular queue */
uint32_t n;
int kind;
int32_t protocol;
uint8_t idle;
bool idle_pending;
//QEMUTimer *idle_timer;
HIDEventFunc event;
};
void hid_init(HIDState *hs, int kind, HIDEventFunc event);
void hid_reset(HIDState *hs);
void hid_free(HIDState *hs);
bool hid_has_events(HIDState *hs);
void hid_set_next_idle(HIDState *hs);
void hid_pointer_activate(HIDState *hs);
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len);
int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len);
int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len);
#endif /* QEMU_HID_H */

View File

@ -0,0 +1,529 @@
#include "input-keymap-linux-to-qcode.h"
const QKeyCode qemu_input_map_linux_to_qcode[] = {
Q_KEY_CODE_UNMAPPED, /* linux:0 (KEY_RESERVED) -> linux:0 (KEY_RESERVED) -> qcode:Q_KEY_CODE_UNMAPPED (unmapped) */
Q_KEY_CODE_ESC, /* linux:1 (KEY_ESC) -> linux:1 (KEY_ESC) -> qcode:Q_KEY_CODE_ESC (esc) */
Q_KEY_CODE_1, /* linux:2 (KEY_1) -> linux:2 (KEY_1) -> qcode:Q_KEY_CODE_1 (1) */
Q_KEY_CODE_2, /* linux:3 (KEY_2) -> linux:3 (KEY_2) -> qcode:Q_KEY_CODE_2 (2) */
Q_KEY_CODE_3, /* linux:4 (KEY_3) -> linux:4 (KEY_3) -> qcode:Q_KEY_CODE_3 (3) */
Q_KEY_CODE_4, /* linux:5 (KEY_4) -> linux:5 (KEY_4) -> qcode:Q_KEY_CODE_4 (4) */
Q_KEY_CODE_5, /* linux:6 (KEY_5) -> linux:6 (KEY_5) -> qcode:Q_KEY_CODE_5 (5) */
Q_KEY_CODE_6, /* linux:7 (KEY_6) -> linux:7 (KEY_6) -> qcode:Q_KEY_CODE_6 (6) */
Q_KEY_CODE_7, /* linux:8 (KEY_7) -> linux:8 (KEY_7) -> qcode:Q_KEY_CODE_7 (7) */
Q_KEY_CODE_8, /* linux:9 (KEY_8) -> linux:9 (KEY_8) -> qcode:Q_KEY_CODE_8 (8) */
Q_KEY_CODE_9, /* linux:10 (KEY_9) -> linux:10 (KEY_9) -> qcode:Q_KEY_CODE_9 (9) */
Q_KEY_CODE_0, /* linux:11 (KEY_0) -> linux:11 (KEY_0) -> qcode:Q_KEY_CODE_0 (0) */
Q_KEY_CODE_MINUS, /* linux:12 (KEY_MINUS) -> linux:12 (KEY_MINUS) -> qcode:Q_KEY_CODE_MINUS (minus) */
Q_KEY_CODE_EQUAL, /* linux:13 (KEY_EQUAL) -> linux:13 (KEY_EQUAL) -> qcode:Q_KEY_CODE_EQUAL (equal) */
Q_KEY_CODE_BACKSPACE, /* linux:14 (KEY_BACKSPACE) -> linux:14 (KEY_BACKSPACE) -> qcode:Q_KEY_CODE_BACKSPACE (backspace) */
Q_KEY_CODE_TAB, /* linux:15 (KEY_TAB) -> linux:15 (KEY_TAB) -> qcode:Q_KEY_CODE_TAB (tab) */
Q_KEY_CODE_Q, /* linux:16 (KEY_Q) -> linux:16 (KEY_Q) -> qcode:Q_KEY_CODE_Q (q) */
Q_KEY_CODE_W, /* linux:17 (KEY_W) -> linux:17 (KEY_W) -> qcode:Q_KEY_CODE_W (w) */
Q_KEY_CODE_E, /* linux:18 (KEY_E) -> linux:18 (KEY_E) -> qcode:Q_KEY_CODE_E (e) */
Q_KEY_CODE_R, /* linux:19 (KEY_R) -> linux:19 (KEY_R) -> qcode:Q_KEY_CODE_R (r) */
Q_KEY_CODE_T, /* linux:20 (KEY_T) -> linux:20 (KEY_T) -> qcode:Q_KEY_CODE_T (t) */
Q_KEY_CODE_Y, /* linux:21 (KEY_Y) -> linux:21 (KEY_Y) -> qcode:Q_KEY_CODE_Y (y) */
Q_KEY_CODE_U, /* linux:22 (KEY_U) -> linux:22 (KEY_U) -> qcode:Q_KEY_CODE_U (u) */
Q_KEY_CODE_I, /* linux:23 (KEY_I) -> linux:23 (KEY_I) -> qcode:Q_KEY_CODE_I (i) */
Q_KEY_CODE_O, /* linux:24 (KEY_O) -> linux:24 (KEY_O) -> qcode:Q_KEY_CODE_O (o) */
Q_KEY_CODE_P, /* linux:25 (KEY_P) -> linux:25 (KEY_P) -> qcode:Q_KEY_CODE_P (p) */
Q_KEY_CODE_BRACKET_LEFT, /* linux:26 (KEY_LEFTBRACE) -> linux:26 (KEY_LEFTBRACE) -> qcode:Q_KEY_CODE_BRACKET_LEFT (bracket_left) */
Q_KEY_CODE_BRACKET_RIGHT, /* linux:27 (KEY_RIGHTBRACE) -> linux:27 (KEY_RIGHTBRACE) -> qcode:Q_KEY_CODE_BRACKET_RIGHT (bracket_right) */
Q_KEY_CODE_RET, /* linux:28 (KEY_ENTER) -> linux:28 (KEY_ENTER) -> qcode:Q_KEY_CODE_RET (ret) */
Q_KEY_CODE_CTRL, /* linux:29 (KEY_LEFTCTRL) -> linux:29 (KEY_LEFTCTRL) -> qcode:Q_KEY_CODE_CTRL (ctrl) */
Q_KEY_CODE_A, /* linux:30 (KEY_A) -> linux:30 (KEY_A) -> qcode:Q_KEY_CODE_A (a) */
Q_KEY_CODE_S, /* linux:31 (KEY_S) -> linux:31 (KEY_S) -> qcode:Q_KEY_CODE_S (s) */
Q_KEY_CODE_D, /* linux:32 (KEY_D) -> linux:32 (KEY_D) -> qcode:Q_KEY_CODE_D (d) */
Q_KEY_CODE_F, /* linux:33 (KEY_F) -> linux:33 (KEY_F) -> qcode:Q_KEY_CODE_F (f) */
Q_KEY_CODE_G, /* linux:34 (KEY_G) -> linux:34 (KEY_G) -> qcode:Q_KEY_CODE_G (g) */
Q_KEY_CODE_H, /* linux:35 (KEY_H) -> linux:35 (KEY_H) -> qcode:Q_KEY_CODE_H (h) */
Q_KEY_CODE_J, /* linux:36 (KEY_J) -> linux:36 (KEY_J) -> qcode:Q_KEY_CODE_J (j) */
Q_KEY_CODE_K, /* linux:37 (KEY_K) -> linux:37 (KEY_K) -> qcode:Q_KEY_CODE_K (k) */
Q_KEY_CODE_L, /* linux:38 (KEY_L) -> linux:38 (KEY_L) -> qcode:Q_KEY_CODE_L (l) */
Q_KEY_CODE_SEMICOLON, /* linux:39 (KEY_SEMICOLON) -> linux:39 (KEY_SEMICOLON) -> qcode:Q_KEY_CODE_SEMICOLON (semicolon) */
Q_KEY_CODE_APOSTROPHE, /* linux:40 (KEY_APOSTROPHE) -> linux:40 (KEY_APOSTROPHE) -> qcode:Q_KEY_CODE_APOSTROPHE (apostrophe) */
Q_KEY_CODE_GRAVE_ACCENT, /* linux:41 (KEY_GRAVE) -> linux:41 (KEY_GRAVE) -> qcode:Q_KEY_CODE_GRAVE_ACCENT (grave_accent) */
Q_KEY_CODE_SHIFT, /* linux:42 (KEY_LEFTSHIFT) -> linux:42 (KEY_LEFTSHIFT) -> qcode:Q_KEY_CODE_SHIFT (shift) */
Q_KEY_CODE_BACKSLASH, /* linux:43 (KEY_BACKSLASH) -> linux:43 (KEY_BACKSLASH) -> qcode:Q_KEY_CODE_BACKSLASH (backslash) */
Q_KEY_CODE_Z, /* linux:44 (KEY_Z) -> linux:44 (KEY_Z) -> qcode:Q_KEY_CODE_Z (z) */
Q_KEY_CODE_X, /* linux:45 (KEY_X) -> linux:45 (KEY_X) -> qcode:Q_KEY_CODE_X (x) */
Q_KEY_CODE_C, /* linux:46 (KEY_C) -> linux:46 (KEY_C) -> qcode:Q_KEY_CODE_C (c) */
Q_KEY_CODE_V, /* linux:47 (KEY_V) -> linux:47 (KEY_V) -> qcode:Q_KEY_CODE_V (v) */
Q_KEY_CODE_B, /* linux:48 (KEY_B) -> linux:48 (KEY_B) -> qcode:Q_KEY_CODE_B (b) */
Q_KEY_CODE_N, /* linux:49 (KEY_N) -> linux:49 (KEY_N) -> qcode:Q_KEY_CODE_N (n) */
Q_KEY_CODE_M, /* linux:50 (KEY_M) -> linux:50 (KEY_M) -> qcode:Q_KEY_CODE_M (m) */
Q_KEY_CODE_COMMA, /* linux:51 (KEY_COMMA) -> linux:51 (KEY_COMMA) -> qcode:Q_KEY_CODE_COMMA (comma) */
Q_KEY_CODE_DOT, /* linux:52 (KEY_DOT) -> linux:52 (KEY_DOT) -> qcode:Q_KEY_CODE_DOT (dot) */
Q_KEY_CODE_SLASH, /* linux:53 (KEY_SLASH) -> linux:53 (KEY_SLASH) -> qcode:Q_KEY_CODE_SLASH (slash) */
Q_KEY_CODE_SHIFT_R, /* linux:54 (KEY_RIGHTSHIFT) -> linux:54 (KEY_RIGHTSHIFT) -> qcode:Q_KEY_CODE_SHIFT_R (shift_r) */
Q_KEY_CODE_KP_MULTIPLY, /* linux:55 (KEY_KPASTERISK) -> linux:55 (KEY_KPASTERISK) -> qcode:Q_KEY_CODE_KP_MULTIPLY (kp_multiply) */
Q_KEY_CODE_ALT, /* linux:56 (KEY_LEFTALT) -> linux:56 (KEY_LEFTALT) -> qcode:Q_KEY_CODE_ALT (alt) */
Q_KEY_CODE_SPC, /* linux:57 (KEY_SPACE) -> linux:57 (KEY_SPACE) -> qcode:Q_KEY_CODE_SPC (spc) */
Q_KEY_CODE_CAPS_LOCK, /* linux:58 (KEY_CAPSLOCK) -> linux:58 (KEY_CAPSLOCK) -> qcode:Q_KEY_CODE_CAPS_LOCK (caps_lock) */
Q_KEY_CODE_F1, /* linux:59 (KEY_F1) -> linux:59 (KEY_F1) -> qcode:Q_KEY_CODE_F1 (f1) */
Q_KEY_CODE_F2, /* linux:60 (KEY_F2) -> linux:60 (KEY_F2) -> qcode:Q_KEY_CODE_F2 (f2) */
Q_KEY_CODE_F3, /* linux:61 (KEY_F3) -> linux:61 (KEY_F3) -> qcode:Q_KEY_CODE_F3 (f3) */
Q_KEY_CODE_F4, /* linux:62 (KEY_F4) -> linux:62 (KEY_F4) -> qcode:Q_KEY_CODE_F4 (f4) */
Q_KEY_CODE_F5, /* linux:63 (KEY_F5) -> linux:63 (KEY_F5) -> qcode:Q_KEY_CODE_F5 (f5) */
Q_KEY_CODE_F6, /* linux:64 (KEY_F6) -> linux:64 (KEY_F6) -> qcode:Q_KEY_CODE_F6 (f6) */
Q_KEY_CODE_F7, /* linux:65 (KEY_F7) -> linux:65 (KEY_F7) -> qcode:Q_KEY_CODE_F7 (f7) */
Q_KEY_CODE_F8, /* linux:66 (KEY_F8) -> linux:66 (KEY_F8) -> qcode:Q_KEY_CODE_F8 (f8) */
Q_KEY_CODE_F9, /* linux:67 (KEY_F9) -> linux:67 (KEY_F9) -> qcode:Q_KEY_CODE_F9 (f9) */
Q_KEY_CODE_F10, /* linux:68 (KEY_F10) -> linux:68 (KEY_F10) -> qcode:Q_KEY_CODE_F10 (f10) */
Q_KEY_CODE_NUM_LOCK, /* linux:69 (KEY_NUMLOCK) -> linux:69 (KEY_NUMLOCK) -> qcode:Q_KEY_CODE_NUM_LOCK (num_lock) */
Q_KEY_CODE_SCROLL_LOCK, /* linux:70 (KEY_SCROLLLOCK) -> linux:70 (KEY_SCROLLLOCK) -> qcode:Q_KEY_CODE_SCROLL_LOCK (scroll_lock) */
Q_KEY_CODE_KP_7, /* linux:71 (KEY_KP7) -> linux:71 (KEY_KP7) -> qcode:Q_KEY_CODE_KP_7 (kp_7) */
Q_KEY_CODE_KP_8, /* linux:72 (KEY_KP8) -> linux:72 (KEY_KP8) -> qcode:Q_KEY_CODE_KP_8 (kp_8) */
Q_KEY_CODE_KP_9, /* linux:73 (KEY_KP9) -> linux:73 (KEY_KP9) -> qcode:Q_KEY_CODE_KP_9 (kp_9) */
Q_KEY_CODE_KP_SUBTRACT, /* linux:74 (KEY_KPMINUS) -> linux:74 (KEY_KPMINUS) -> qcode:Q_KEY_CODE_KP_SUBTRACT (kp_subtract) */
Q_KEY_CODE_KP_4, /* linux:75 (KEY_KP4) -> linux:75 (KEY_KP4) -> qcode:Q_KEY_CODE_KP_4 (kp_4) */
Q_KEY_CODE_KP_5, /* linux:76 (KEY_KP5) -> linux:76 (KEY_KP5) -> qcode:Q_KEY_CODE_KP_5 (kp_5) */
Q_KEY_CODE_KP_6, /* linux:77 (KEY_KP6) -> linux:77 (KEY_KP6) -> qcode:Q_KEY_CODE_KP_6 (kp_6) */
Q_KEY_CODE_KP_ADD, /* linux:78 (KEY_KPPLUS) -> linux:78 (KEY_KPPLUS) -> qcode:Q_KEY_CODE_KP_ADD (kp_add) */
Q_KEY_CODE_KP_1, /* linux:79 (KEY_KP1) -> linux:79 (KEY_KP1) -> qcode:Q_KEY_CODE_KP_1 (kp_1) */
Q_KEY_CODE_KP_2, /* linux:80 (KEY_KP2) -> linux:80 (KEY_KP2) -> qcode:Q_KEY_CODE_KP_2 (kp_2) */
Q_KEY_CODE_KP_3, /* linux:81 (KEY_KP3) -> linux:81 (KEY_KP3) -> qcode:Q_KEY_CODE_KP_3 (kp_3) */
Q_KEY_CODE_KP_0, /* linux:82 (KEY_KP0) -> linux:82 (KEY_KP0) -> qcode:Q_KEY_CODE_KP_0 (kp_0) */
Q_KEY_CODE_KP_DECIMAL, /* linux:83 (KEY_KPDOT) -> linux:83 (KEY_KPDOT) -> qcode:Q_KEY_CODE_KP_DECIMAL (kp_decimal) */
Q_KEY_CODE_UNMAPPED, /* linux:84 (unnamed) -> linux:84 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:85 (KEY_ZENKAKUHANKAKU) -> linux:85 (KEY_ZENKAKUHANKAKU) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_LESS, /* linux:86 (KEY_102ND) -> linux:86 (KEY_102ND) -> qcode:Q_KEY_CODE_LESS (less) */
Q_KEY_CODE_F11, /* linux:87 (KEY_F11) -> linux:87 (KEY_F11) -> qcode:Q_KEY_CODE_F11 (f11) */
Q_KEY_CODE_F12, /* linux:88 (KEY_F12) -> linux:88 (KEY_F12) -> qcode:Q_KEY_CODE_F12 (f12) */
Q_KEY_CODE_RO, /* linux:89 (KEY_RO) -> linux:89 (KEY_RO) -> qcode:Q_KEY_CODE_RO (ro) */
Q_KEY_CODE_UNMAPPED, /* linux:90 (KEY_KATAKANA) -> linux:90 (KEY_KATAKANA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_HIRAGANA, /* linux:91 (KEY_HIRAGANA) -> linux:91 (KEY_HIRAGANA) -> qcode:Q_KEY_CODE_HIRAGANA (hiragana) */
Q_KEY_CODE_HENKAN, /* linux:92 (KEY_HENKAN) -> linux:92 (KEY_HENKAN) -> qcode:Q_KEY_CODE_HENKAN (henkan) */
Q_KEY_CODE_KATAKANAHIRAGANA, /* linux:93 (KEY_KATAKANAHIRAGANA) -> linux:93 (KEY_KATAKANAHIRAGANA) -> qcode:Q_KEY_CODE_KATAKANAHIRAGANA (katakanahiragana) */
Q_KEY_CODE_MUHENKAN, /* linux:94 (KEY_MUHENKAN) -> linux:94 (KEY_MUHENKAN) -> qcode:Q_KEY_CODE_MUHENKAN (muhenkan) */
Q_KEY_CODE_UNMAPPED, /* linux:95 (KEY_KPJPCOMMA) -> linux:95 (KEY_KPJPCOMMA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_KP_ENTER, /* linux:96 (KEY_KPENTER) -> linux:96 (KEY_KPENTER) -> qcode:Q_KEY_CODE_KP_ENTER (kp_enter) */
Q_KEY_CODE_CTRL_R, /* linux:97 (KEY_RIGHTCTRL) -> linux:97 (KEY_RIGHTCTRL) -> qcode:Q_KEY_CODE_CTRL_R (ctrl_r) */
Q_KEY_CODE_KP_DIVIDE, /* linux:98 (KEY_KPSLASH) -> linux:98 (KEY_KPSLASH) -> qcode:Q_KEY_CODE_KP_DIVIDE (kp_divide) */
Q_KEY_CODE_SYSRQ, /* linux:99 (KEY_SYSRQ) -> linux:99 (KEY_SYSRQ) -> qcode:Q_KEY_CODE_SYSRQ (sysrq) */
Q_KEY_CODE_ALT_R, /* linux:100 (KEY_RIGHTALT) -> linux:100 (KEY_RIGHTALT) -> qcode:Q_KEY_CODE_ALT_R (alt_r) */
Q_KEY_CODE_LF, /* linux:101 (KEY_LINEFEED) -> linux:101 (KEY_LINEFEED) -> qcode:Q_KEY_CODE_LF (lf) */
Q_KEY_CODE_HOME, /* linux:102 (KEY_HOME) -> linux:102 (KEY_HOME) -> qcode:Q_KEY_CODE_HOME (home) */
Q_KEY_CODE_UP, /* linux:103 (KEY_UP) -> linux:103 (KEY_UP) -> qcode:Q_KEY_CODE_UP (up) */
Q_KEY_CODE_PGUP, /* linux:104 (KEY_PAGEUP) -> linux:104 (KEY_PAGEUP) -> qcode:Q_KEY_CODE_PGUP (pgup) */
Q_KEY_CODE_LEFT, /* linux:105 (KEY_LEFT) -> linux:105 (KEY_LEFT) -> qcode:Q_KEY_CODE_LEFT (left) */
Q_KEY_CODE_RIGHT, /* linux:106 (KEY_RIGHT) -> linux:106 (KEY_RIGHT) -> qcode:Q_KEY_CODE_RIGHT (right) */
Q_KEY_CODE_END, /* linux:107 (KEY_END) -> linux:107 (KEY_END) -> qcode:Q_KEY_CODE_END (end) */
Q_KEY_CODE_DOWN, /* linux:108 (KEY_DOWN) -> linux:108 (KEY_DOWN) -> qcode:Q_KEY_CODE_DOWN (down) */
Q_KEY_CODE_PGDN, /* linux:109 (KEY_PAGEDOWN) -> linux:109 (KEY_PAGEDOWN) -> qcode:Q_KEY_CODE_PGDN (pgdn) */
Q_KEY_CODE_INSERT, /* linux:110 (KEY_INSERT) -> linux:110 (KEY_INSERT) -> qcode:Q_KEY_CODE_INSERT (insert) */
Q_KEY_CODE_DELETE, /* linux:111 (KEY_DELETE) -> linux:111 (KEY_DELETE) -> qcode:Q_KEY_CODE_DELETE (delete) */
Q_KEY_CODE_UNMAPPED, /* linux:112 (KEY_MACRO) -> linux:112 (KEY_MACRO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_AUDIOMUTE, /* linux:113 (KEY_MUTE) -> linux:113 (KEY_MUTE) -> qcode:Q_KEY_CODE_AUDIOMUTE (audiomute) */
Q_KEY_CODE_VOLUMEDOWN, /* linux:114 (KEY_VOLUMEDOWN) -> linux:114 (KEY_VOLUMEDOWN) -> qcode:Q_KEY_CODE_VOLUMEDOWN (volumedown) */
Q_KEY_CODE_VOLUMEUP, /* linux:115 (KEY_VOLUMEUP) -> linux:115 (KEY_VOLUMEUP) -> qcode:Q_KEY_CODE_VOLUMEUP (volumeup) */
Q_KEY_CODE_POWER, /* linux:116 (KEY_POWER) -> linux:116 (KEY_POWER) -> qcode:Q_KEY_CODE_POWER (power) */
Q_KEY_CODE_KP_EQUALS, /* linux:117 (KEY_KPEQUAL) -> linux:117 (KEY_KPEQUAL) -> qcode:Q_KEY_CODE_KP_EQUALS (kp_equals) */
Q_KEY_CODE_UNMAPPED, /* linux:118 (KEY_KPPLUSMINUS) -> linux:118 (KEY_KPPLUSMINUS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_PAUSE, /* linux:119 (KEY_PAUSE) -> linux:119 (KEY_PAUSE) -> qcode:Q_KEY_CODE_PAUSE (pause) */
Q_KEY_CODE_UNMAPPED, /* linux:120 (KEY_SCALE) -> linux:120 (KEY_SCALE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_KP_COMMA, /* linux:121 (KEY_KPCOMMA) -> linux:121 (KEY_KPCOMMA) -> qcode:Q_KEY_CODE_KP_COMMA (kp_comma) */
Q_KEY_CODE_UNMAPPED, /* linux:122 (KEY_HANGEUL) -> linux:122 (KEY_HANGEUL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:123 (KEY_HANJA) -> linux:123 (KEY_HANJA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_YEN, /* linux:124 (KEY_YEN) -> linux:124 (KEY_YEN) -> qcode:Q_KEY_CODE_YEN (yen) */
Q_KEY_CODE_META_L, /* linux:125 (KEY_LEFTMETA) -> linux:125 (KEY_LEFTMETA) -> qcode:Q_KEY_CODE_META_L (meta_l) */
Q_KEY_CODE_META_R, /* linux:126 (KEY_RIGHTMETA) -> linux:126 (KEY_RIGHTMETA) -> qcode:Q_KEY_CODE_META_R (meta_r) */
Q_KEY_CODE_COMPOSE, /* linux:127 (KEY_COMPOSE) -> linux:127 (KEY_COMPOSE) -> qcode:Q_KEY_CODE_COMPOSE (compose) */
Q_KEY_CODE_STOP, /* linux:128 (KEY_STOP) -> linux:128 (KEY_STOP) -> qcode:Q_KEY_CODE_STOP (stop) */
Q_KEY_CODE_AGAIN, /* linux:129 (KEY_AGAIN) -> linux:129 (KEY_AGAIN) -> qcode:Q_KEY_CODE_AGAIN (again) */
Q_KEY_CODE_PROPS, /* linux:130 (KEY_PROPS) -> linux:130 (KEY_PROPS) -> qcode:Q_KEY_CODE_PROPS (props) */
Q_KEY_CODE_UNDO, /* linux:131 (KEY_UNDO) -> linux:131 (KEY_UNDO) -> qcode:Q_KEY_CODE_UNDO (undo) */
Q_KEY_CODE_FRONT, /* linux:132 (KEY_FRONT) -> linux:132 (KEY_FRONT) -> qcode:Q_KEY_CODE_FRONT (front) */
Q_KEY_CODE_COPY, /* linux:133 (KEY_COPY) -> linux:133 (KEY_COPY) -> qcode:Q_KEY_CODE_COPY (copy) */
Q_KEY_CODE_OPEN, /* linux:134 (KEY_OPEN) -> linux:134 (KEY_OPEN) -> qcode:Q_KEY_CODE_OPEN (open) */
Q_KEY_CODE_PASTE, /* linux:135 (KEY_PASTE) -> linux:135 (KEY_PASTE) -> qcode:Q_KEY_CODE_PASTE (paste) */
Q_KEY_CODE_FIND, /* linux:136 (KEY_FIND) -> linux:136 (KEY_FIND) -> qcode:Q_KEY_CODE_FIND (find) */
Q_KEY_CODE_CUT, /* linux:137 (KEY_CUT) -> linux:137 (KEY_CUT) -> qcode:Q_KEY_CODE_CUT (cut) */
Q_KEY_CODE_HELP, /* linux:138 (KEY_HELP) -> linux:138 (KEY_HELP) -> qcode:Q_KEY_CODE_HELP (help) */
Q_KEY_CODE_MENU, /* linux:139 (KEY_MENU) -> linux:139 (KEY_MENU) -> qcode:Q_KEY_CODE_MENU (menu) */
Q_KEY_CODE_CALCULATOR, /* linux:140 (KEY_CALC) -> linux:140 (KEY_CALC) -> qcode:Q_KEY_CODE_CALCULATOR (calculator) */
Q_KEY_CODE_UNMAPPED, /* linux:141 (KEY_SETUP) -> linux:141 (KEY_SETUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_SLEEP, /* linux:142 (KEY_SLEEP) -> linux:142 (KEY_SLEEP) -> qcode:Q_KEY_CODE_SLEEP (sleep) */
Q_KEY_CODE_WAKE, /* linux:143 (KEY_WAKEUP) -> linux:143 (KEY_WAKEUP) -> qcode:Q_KEY_CODE_WAKE (wake) */
Q_KEY_CODE_UNMAPPED, /* linux:144 (KEY_FILE) -> linux:144 (KEY_FILE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:145 (KEY_SENDFILE) -> linux:145 (KEY_SENDFILE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:146 (KEY_DELETEFILE) -> linux:146 (KEY_DELETEFILE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:147 (KEY_XFER) -> linux:147 (KEY_XFER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:148 (KEY_PROG1) -> linux:148 (KEY_PROG1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:149 (KEY_PROG2) -> linux:149 (KEY_PROG2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:150 (KEY_WWW) -> linux:150 (KEY_WWW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:151 (KEY_MSDOS) -> linux:151 (KEY_MSDOS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:152 (KEY_SCREENLOCK) -> linux:152 (KEY_SCREENLOCK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:153 (KEY_DIRECTION) -> linux:153 (KEY_DIRECTION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:154 (KEY_CYCLEWINDOWS) -> linux:154 (KEY_CYCLEWINDOWS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_MAIL, /* linux:155 (KEY_MAIL) -> linux:155 (KEY_MAIL) -> qcode:Q_KEY_CODE_MAIL (mail) */
Q_KEY_CODE_AC_BOOKMARKS, /* linux:156 (KEY_BOOKMARKS) -> linux:156 (KEY_BOOKMARKS) -> qcode:Q_KEY_CODE_AC_BOOKMARKS (ac_bookmarks) */
Q_KEY_CODE_COMPUTER, /* linux:157 (KEY_COMPUTER) -> linux:157 (KEY_COMPUTER) -> qcode:Q_KEY_CODE_COMPUTER (computer) */
Q_KEY_CODE_AC_BACK, /* linux:158 (KEY_BACK) -> linux:158 (KEY_BACK) -> qcode:Q_KEY_CODE_AC_BACK (ac_back) */
Q_KEY_CODE_AC_FORWARD, /* linux:159 (KEY_FORWARD) -> linux:159 (KEY_FORWARD) -> qcode:Q_KEY_CODE_AC_FORWARD (ac_forward) */
Q_KEY_CODE_UNMAPPED, /* linux:160 (KEY_CLOSECD) -> linux:160 (KEY_CLOSECD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:161 (KEY_EJECTCD) -> linux:161 (KEY_EJECTCD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:162 (KEY_EJECTCLOSECD) -> linux:162 (KEY_EJECTCLOSECD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_AUDIONEXT, /* linux:163 (KEY_NEXTSONG) -> linux:163 (KEY_NEXTSONG) -> qcode:Q_KEY_CODE_AUDIONEXT (audionext) */
Q_KEY_CODE_AUDIOPLAY, /* linux:164 (KEY_PLAYPAUSE) -> linux:164 (KEY_PLAYPAUSE) -> qcode:Q_KEY_CODE_AUDIOPLAY (audioplay) */
Q_KEY_CODE_AUDIOPREV, /* linux:165 (KEY_PREVIOUSSONG) -> linux:165 (KEY_PREVIOUSSONG) -> qcode:Q_KEY_CODE_AUDIOPREV (audioprev) */
Q_KEY_CODE_AUDIOSTOP, /* linux:166 (KEY_STOPCD) -> linux:166 (KEY_STOPCD) -> qcode:Q_KEY_CODE_AUDIOSTOP (audiostop) */
Q_KEY_CODE_UNMAPPED, /* linux:167 (KEY_RECORD) -> linux:167 (KEY_RECORD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:168 (KEY_REWIND) -> linux:168 (KEY_REWIND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:169 (KEY_PHONE) -> linux:169 (KEY_PHONE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:170 (KEY_ISO) -> linux:170 (KEY_ISO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:171 (KEY_CONFIG) -> linux:171 (KEY_CONFIG) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_AC_HOME, /* linux:172 (KEY_HOMEPAGE) -> linux:172 (KEY_HOMEPAGE) -> qcode:Q_KEY_CODE_AC_HOME (ac_home) */
Q_KEY_CODE_AC_REFRESH, /* linux:173 (KEY_REFRESH) -> linux:173 (KEY_REFRESH) -> qcode:Q_KEY_CODE_AC_REFRESH (ac_refresh) */
Q_KEY_CODE_UNMAPPED, /* linux:174 (KEY_EXIT) -> linux:174 (KEY_EXIT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:175 (KEY_MOVE) -> linux:175 (KEY_MOVE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:176 (KEY_EDIT) -> linux:176 (KEY_EDIT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:177 (KEY_SCROLLUP) -> linux:177 (KEY_SCROLLUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:178 (KEY_SCROLLDOWN) -> linux:178 (KEY_SCROLLDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:179 (KEY_KPLEFTPAREN) -> linux:179 (KEY_KPLEFTPAREN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:180 (KEY_KPRIGHTPAREN) -> linux:180 (KEY_KPRIGHTPAREN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:181 (KEY_NEW) -> linux:181 (KEY_NEW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:182 (KEY_REDO) -> linux:182 (KEY_REDO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:183 (KEY_F13) -> linux:183 (KEY_F13) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:184 (KEY_F14) -> linux:184 (KEY_F14) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:185 (KEY_F15) -> linux:185 (KEY_F15) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:186 (KEY_F16) -> linux:186 (KEY_F16) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:187 (KEY_F17) -> linux:187 (KEY_F17) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:188 (KEY_F18) -> linux:188 (KEY_F18) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:189 (KEY_F19) -> linux:189 (KEY_F19) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:190 (KEY_F20) -> linux:190 (KEY_F20) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:191 (KEY_F21) -> linux:191 (KEY_F21) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:192 (KEY_F22) -> linux:192 (KEY_F22) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:193 (KEY_F23) -> linux:193 (KEY_F23) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:194 (KEY_F24) -> linux:194 (KEY_F24) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:195 (unnamed) -> linux:195 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:196 (unnamed) -> linux:196 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:197 (unnamed) -> linux:197 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:198 (unnamed) -> linux:198 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:199 (unnamed) -> linux:199 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:200 (KEY_PLAYCD) -> linux:200 (KEY_PLAYCD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:201 (KEY_PAUSECD) -> linux:201 (KEY_PAUSECD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:202 (KEY_PROG3) -> linux:202 (KEY_PROG3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:203 (KEY_PROG4) -> linux:203 (KEY_PROG4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:204 (KEY_DASHBOARD) -> linux:204 (KEY_DASHBOARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:205 (KEY_SUSPEND) -> linux:205 (KEY_SUSPEND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:206 (KEY_CLOSE) -> linux:206 (KEY_CLOSE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:207 (KEY_PLAY) -> linux:207 (KEY_PLAY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:208 (KEY_FASTFORWARD) -> linux:208 (KEY_FASTFORWARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:209 (KEY_BASSBOOST) -> linux:209 (KEY_BASSBOOST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:210 (KEY_PRINT) -> linux:210 (KEY_PRINT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:211 (KEY_HP) -> linux:211 (KEY_HP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:212 (KEY_CAMERA) -> linux:212 (KEY_CAMERA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:213 (KEY_SOUND) -> linux:213 (KEY_SOUND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:214 (KEY_QUESTION) -> linux:214 (KEY_QUESTION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:215 (KEY_EMAIL) -> linux:215 (KEY_EMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:216 (KEY_CHAT) -> linux:216 (KEY_CHAT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:217 (KEY_SEARCH) -> linux:217 (KEY_SEARCH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:218 (KEY_CONNECT) -> linux:218 (KEY_CONNECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:219 (KEY_FINANCE) -> linux:219 (KEY_FINANCE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:220 (KEY_SPORT) -> linux:220 (KEY_SPORT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:221 (KEY_SHOP) -> linux:221 (KEY_SHOP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:222 (KEY_ALTERASE) -> linux:222 (KEY_ALTERASE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:223 (KEY_CANCEL) -> linux:223 (KEY_CANCEL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:224 (KEY_BRIGHTNESSDOWN) -> linux:224 (KEY_BRIGHTNESSDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:225 (KEY_BRIGHTNESSUP) -> linux:225 (KEY_BRIGHTNESSUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_MEDIASELECT, /* linux:226 (KEY_MEDIA) -> linux:226 (KEY_MEDIA) -> qcode:Q_KEY_CODE_MEDIASELECT (mediaselect) */
Q_KEY_CODE_UNMAPPED, /* linux:227 (KEY_SWITCHVIDEOMODE) -> linux:227 (KEY_SWITCHVIDEOMODE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:228 (KEY_KBDILLUMTOGGLE) -> linux:228 (KEY_KBDILLUMTOGGLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:229 (KEY_KBDILLUMDOWN) -> linux:229 (KEY_KBDILLUMDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:230 (KEY_KBDILLUMUP) -> linux:230 (KEY_KBDILLUMUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:231 (KEY_SEND) -> linux:231 (KEY_SEND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:232 (KEY_REPLY) -> linux:232 (KEY_REPLY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:233 (KEY_FORWARDMAIL) -> linux:233 (KEY_FORWARDMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:234 (KEY_SAVE) -> linux:234 (KEY_SAVE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:235 (KEY_DOCUMENTS) -> linux:235 (KEY_DOCUMENTS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:236 (KEY_BATTERY) -> linux:236 (KEY_BATTERY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:237 (KEY_BLUETOOTH) -> linux:237 (KEY_BLUETOOTH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:238 (KEY_WLAN) -> linux:238 (KEY_WLAN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:239 (KEY_UWB) -> linux:239 (KEY_UWB) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:240 (KEY_UNKNOWN) -> linux:240 (KEY_UNKNOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:241 (KEY_VIDEO_NEXT) -> linux:241 (KEY_VIDEO_NEXT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:242 (KEY_VIDEO_PREV) -> linux:242 (KEY_VIDEO_PREV) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:243 (KEY_BRIGHTNESS_CYCLE) -> linux:243 (KEY_BRIGHTNESS_CYCLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:244 (KEY_BRIGHTNESS_ZERO) -> linux:244 (KEY_BRIGHTNESS_ZERO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:245 (KEY_DISPLAY_OFF) -> linux:245 (KEY_DISPLAY_OFF) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:246 (KEY_WIMAX) -> linux:246 (KEY_WIMAX) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:247 (unnamed) -> linux:247 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:248 (unnamed) -> linux:248 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:249 (unnamed) -> linux:249 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:250 (unnamed) -> linux:250 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:251 (unnamed) -> linux:251 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:252 (unnamed) -> linux:252 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:253 (unnamed) -> linux:253 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:254 (unnamed) -> linux:254 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:255 (unnamed) -> linux:255 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:256 (BTN_0) -> linux:256 (BTN_0) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:257 (BTN_1) -> linux:257 (BTN_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:258 (BTN_2) -> linux:258 (BTN_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:259 (BTN_3) -> linux:259 (BTN_3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:260 (BTN_4) -> linux:260 (BTN_4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:261 (BTN_5) -> linux:261 (BTN_5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:262 (BTN_6) -> linux:262 (BTN_6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:263 (BTN_7) -> linux:263 (BTN_7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:264 (BTN_8) -> linux:264 (BTN_8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:265 (BTN_9) -> linux:265 (BTN_9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:266 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:267 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:268 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:269 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:270 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:271 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:272 (BTN_LEFT) -> linux:272 (BTN_LEFT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:273 (BTN_RIGHT) -> linux:273 (BTN_RIGHT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:274 (BTN_MIDDLE) -> linux:274 (BTN_MIDDLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:275 (BTN_SIDE) -> linux:275 (BTN_SIDE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:276 (BTN_EXTRA) -> linux:276 (BTN_EXTRA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:277 (BTN_FORWARD) -> linux:277 (BTN_FORWARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:278 (BTN_BACK) -> linux:278 (BTN_BACK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:279 (BTN_TASK) -> linux:279 (BTN_TASK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:280 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:281 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:282 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:283 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:284 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:285 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:286 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:287 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:288 (BTN_TRIGGER) -> linux:288 (BTN_TRIGGER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:289 (BTN_THUMB) -> linux:289 (BTN_THUMB) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:290 (BTN_THUMB2) -> linux:290 (BTN_THUMB2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:291 (BTN_TOP) -> linux:291 (BTN_TOP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:292 (BTN_TOP2) -> linux:292 (BTN_TOP2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:293 (BTN_PINKIE) -> linux:293 (BTN_PINKIE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:294 (BTN_BASE) -> linux:294 (BTN_BASE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:295 (BTN_BASE2) -> linux:295 (BTN_BASE2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:296 (BTN_BASE3) -> linux:296 (BTN_BASE3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:297 (BTN_BASE4) -> linux:297 (BTN_BASE4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:298 (BTN_BASE5) -> linux:298 (BTN_BASE5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:299 (BTN_BASE6) -> linux:299 (BTN_BASE6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:300 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:301 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:302 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:303 (BTN_DEAD) -> linux:303 (BTN_DEAD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:304 (BTN_A) -> linux:304 (BTN_A) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:305 (BTN_B) -> linux:305 (BTN_B) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:306 (BTN_C) -> linux:306 (BTN_C) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:307 (BTN_X) -> linux:307 (BTN_X) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:308 (BTN_Y) -> linux:308 (BTN_Y) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:309 (BTN_Z) -> linux:309 (BTN_Z) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:310 (BTN_TL) -> linux:310 (BTN_TL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:311 (BTN_TR) -> linux:311 (BTN_TR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:312 (BTN_TL2) -> linux:312 (BTN_TL2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:313 (BTN_TR2) -> linux:313 (BTN_TR2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:314 (BTN_SELECT) -> linux:314 (BTN_SELECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:315 (BTN_START) -> linux:315 (BTN_START) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:316 (BTN_MODE) -> linux:316 (BTN_MODE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:317 (BTN_THUMBL) -> linux:317 (BTN_THUMBL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:318 (BTN_THUMBR) -> linux:318 (BTN_THUMBR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:319 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:320 (BTN_TOOL_PEN) -> linux:320 (BTN_TOOL_PEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:321 (BTN_TOOL_RUBBER) -> linux:321 (BTN_TOOL_RUBBER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:322 (BTN_TOOL_BRUSH) -> linux:322 (BTN_TOOL_BRUSH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:323 (BTN_TOOL_PENCIL) -> linux:323 (BTN_TOOL_PENCIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:324 (BTN_TOOL_AIRBRUSH) -> linux:324 (BTN_TOOL_AIRBRUSH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:325 (BTN_TOOL_FINGER) -> linux:325 (BTN_TOOL_FINGER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:326 (BTN_TOOL_MOUSE) -> linux:326 (BTN_TOOL_MOUSE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:327 (BTN_TOOL_LENS) -> linux:327 (BTN_TOOL_LENS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:328 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:329 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:330 (BTN_TOUCH) -> linux:330 (BTN_TOUCH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:331 (BTN_STYLUS) -> linux:331 (BTN_STYLUS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:332 (BTN_STYLUS2) -> linux:332 (BTN_STYLUS2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:333 (BTN_TOOL_DOUBLETAP) -> linux:333 (BTN_TOOL_DOUBLETAP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:334 (BTN_TOOL_TRIPLETAP) -> linux:334 (BTN_TOOL_TRIPLETAP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:335 (BTN_TOOL_QUADTAP) -> linux:335 (BTN_TOOL_QUADTAP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:336 (BTN_GEAR_DOWN) -> linux:336 (BTN_GEAR_DOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:337 (BTN_GEAR_UP) -> linux:337 (BTN_GEAR_UP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:338 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:339 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:340 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:341 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:342 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:343 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:344 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:345 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:346 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:347 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:348 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:349 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:350 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:351 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:352 (KEY_OK) -> linux:352 (KEY_OK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:353 (KEY_SELECT) -> linux:353 (KEY_SELECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:354 (KEY_GOTO) -> linux:354 (KEY_GOTO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:355 (KEY_CLEAR) -> linux:355 (KEY_CLEAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:356 (KEY_POWER2) -> linux:356 (KEY_POWER2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:357 (KEY_OPTION) -> linux:357 (KEY_OPTION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:358 (KEY_INFO) -> linux:358 (KEY_INFO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:359 (KEY_TIME) -> linux:359 (KEY_TIME) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:360 (KEY_VENDOR) -> linux:360 (KEY_VENDOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:361 (KEY_ARCHIVE) -> linux:361 (KEY_ARCHIVE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:362 (KEY_PROGRAM) -> linux:362 (KEY_PROGRAM) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:363 (KEY_CHANNEL) -> linux:363 (KEY_CHANNEL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:364 (KEY_FAVORITES) -> linux:364 (KEY_FAVORITES) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:365 (KEY_EPG) -> linux:365 (KEY_EPG) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:366 (KEY_PVR) -> linux:366 (KEY_PVR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:367 (KEY_MHP) -> linux:367 (KEY_MHP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:368 (KEY_LANGUAGE) -> linux:368 (KEY_LANGUAGE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:369 (KEY_TITLE) -> linux:369 (KEY_TITLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:370 (KEY_SUBTITLE) -> linux:370 (KEY_SUBTITLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:371 (KEY_ANGLE) -> linux:371 (KEY_ANGLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:372 (KEY_ZOOM) -> linux:372 (KEY_ZOOM) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:373 (KEY_MODE) -> linux:373 (KEY_MODE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:374 (KEY_KEYBOARD) -> linux:374 (KEY_KEYBOARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:375 (KEY_SCREEN) -> linux:375 (KEY_SCREEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:376 (KEY_PC) -> linux:376 (KEY_PC) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:377 (KEY_TV) -> linux:377 (KEY_TV) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:378 (KEY_TV2) -> linux:378 (KEY_TV2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:379 (KEY_VCR) -> linux:379 (KEY_VCR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:380 (KEY_VCR2) -> linux:380 (KEY_VCR2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:381 (KEY_SAT) -> linux:381 (KEY_SAT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:382 (KEY_SAT2) -> linux:382 (KEY_SAT2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:383 (KEY_CD) -> linux:383 (KEY_CD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:384 (KEY_TAPE) -> linux:384 (KEY_TAPE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:385 (KEY_RADIO) -> linux:385 (KEY_RADIO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:386 (KEY_TUNER) -> linux:386 (KEY_TUNER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:387 (KEY_PLAYER) -> linux:387 (KEY_PLAYER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:388 (KEY_TEXT) -> linux:388 (KEY_TEXT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:389 (KEY_DVD) -> linux:389 (KEY_DVD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:390 (KEY_AUX) -> linux:390 (KEY_AUX) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:391 (KEY_MP3) -> linux:391 (KEY_MP3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:392 (KEY_AUDIO) -> linux:392 (KEY_AUDIO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:393 (KEY_VIDEO) -> linux:393 (KEY_VIDEO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:394 (KEY_DIRECTORY) -> linux:394 (KEY_DIRECTORY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:395 (KEY_LIST) -> linux:395 (KEY_LIST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:396 (KEY_MEMO) -> linux:396 (KEY_MEMO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:397 (KEY_CALENDAR) -> linux:397 (KEY_CALENDAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:398 (KEY_RED) -> linux:398 (KEY_RED) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:399 (KEY_GREEN) -> linux:399 (KEY_GREEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:400 (KEY_YELLOW) -> linux:400 (KEY_YELLOW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:401 (KEY_BLUE) -> linux:401 (KEY_BLUE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:402 (KEY_CHANNELUP) -> linux:402 (KEY_CHANNELUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:403 (KEY_CHANNELDOWN) -> linux:403 (KEY_CHANNELDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:404 (KEY_FIRST) -> linux:404 (KEY_FIRST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:405 (KEY_LAST) -> linux:405 (KEY_LAST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:406 (KEY_AB) -> linux:406 (KEY_AB) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:407 (KEY_NEXT) -> linux:407 (KEY_NEXT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:408 (KEY_RESTART) -> linux:408 (KEY_RESTART) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:409 (KEY_SLOW) -> linux:409 (KEY_SLOW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:410 (KEY_SHUFFLE) -> linux:410 (KEY_SHUFFLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:411 (KEY_BREAK) -> linux:411 (KEY_BREAK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:412 (KEY_PREVIOUS) -> linux:412 (KEY_PREVIOUS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:413 (KEY_DIGITS) -> linux:413 (KEY_DIGITS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:414 (KEY_TEEN) -> linux:414 (KEY_TEEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:415 (KEY_TWEN) -> linux:415 (KEY_TWEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:416 (KEY_VIDEOPHONE) -> linux:416 (KEY_VIDEOPHONE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:417 (KEY_GAMES) -> linux:417 (KEY_GAMES) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:418 (KEY_ZOOMIN) -> linux:418 (KEY_ZOOMIN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:419 (KEY_ZOOMOUT) -> linux:419 (KEY_ZOOMOUT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:420 (KEY_ZOOMRESET) -> linux:420 (KEY_ZOOMRESET) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:421 (KEY_WORDPROCESSOR) -> linux:421 (KEY_WORDPROCESSOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:422 (KEY_EDITOR) -> linux:422 (KEY_EDITOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:423 (KEY_SPREADSHEET) -> linux:423 (KEY_SPREADSHEET) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:424 (KEY_GRAPHICSEDITOR) -> linux:424 (KEY_GRAPHICSEDITOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:425 (KEY_PRESENTATION) -> linux:425 (KEY_PRESENTATION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:426 (KEY_DATABASE) -> linux:426 (KEY_DATABASE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:427 (KEY_NEWS) -> linux:427 (KEY_NEWS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:428 (KEY_VOICEMAIL) -> linux:428 (KEY_VOICEMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:429 (KEY_ADDRESSBOOK) -> linux:429 (KEY_ADDRESSBOOK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:430 (KEY_MESSENGER) -> linux:430 (KEY_MESSENGER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:431 (KEY_DISPLAYTOGGLE) -> linux:431 (KEY_DISPLAYTOGGLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:432 (KEY_SPELLCHECK) -> linux:432 (KEY_SPELLCHECK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:433 (KEY_LOGOFF) -> linux:433 (KEY_LOGOFF) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:434 (KEY_DOLLAR) -> linux:434 (KEY_DOLLAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:435 (KEY_EURO) -> linux:435 (KEY_EURO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:436 (KEY_FRAMEBACK) -> linux:436 (KEY_FRAMEBACK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:437 (KEY_FRAMEFORWARD) -> linux:437 (KEY_FRAMEFORWARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:438 (KEY_CONTEXT_MENU) -> linux:438 (KEY_CONTEXT_MENU) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:439 (KEY_MEDIA_REPEAT) -> linux:439 (KEY_MEDIA_REPEAT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:440 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:441 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:442 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:443 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:444 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:445 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:446 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:447 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:448 (KEY_DEL_EOL) -> linux:448 (KEY_DEL_EOL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:449 (KEY_DEL_EOS) -> linux:449 (KEY_DEL_EOS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:450 (KEY_INS_LINE) -> linux:450 (KEY_INS_LINE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:451 (KEY_DEL_LINE) -> linux:451 (KEY_DEL_LINE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:452 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:453 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:454 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:455 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:456 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:457 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:458 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:459 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:460 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:461 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:462 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:463 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:464 (KEY_FN) -> linux:464 (KEY_FN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:465 (KEY_FN_ESC) -> linux:465 (KEY_FN_ESC) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:466 (KEY_FN_F1) -> linux:466 (KEY_FN_F1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:467 (KEY_FN_F2) -> linux:467 (KEY_FN_F2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:468 (KEY_FN_F3) -> linux:468 (KEY_FN_F3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:469 (KEY_FN_F4) -> linux:469 (KEY_FN_F4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:470 (KEY_FN_F5) -> linux:470 (KEY_FN_F5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:471 (KEY_FN_F6) -> linux:471 (KEY_FN_F6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:472 (KEY_FN_F7) -> linux:472 (KEY_FN_F7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:473 (KEY_FN_F8) -> linux:473 (KEY_FN_F8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:474 (KEY_FN_F9) -> linux:474 (KEY_FN_F9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:475 (KEY_FN_F10) -> linux:475 (KEY_FN_F10) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:476 (KEY_FN_F11) -> linux:476 (KEY_FN_F11) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:477 (KEY_FN_F12) -> linux:477 (KEY_FN_F12) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:478 (KEY_FN_1) -> linux:478 (KEY_FN_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:479 (KEY_FN_2) -> linux:479 (KEY_FN_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:480 (KEY_FN_D) -> linux:480 (KEY_FN_D) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:481 (KEY_FN_E) -> linux:481 (KEY_FN_E) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:482 (KEY_FN_F) -> linux:482 (KEY_FN_F) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:483 (KEY_FN_S) -> linux:483 (KEY_FN_S) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:484 (KEY_FN_B) -> linux:484 (KEY_FN_B) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:485 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:486 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:487 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:488 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:489 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:490 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:491 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:492 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:493 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:494 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:495 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:496 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:497 (KEY_BRL_DOT1) -> linux:497 (KEY_BRL_DOT1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:498 (KEY_BRL_DOT2) -> linux:498 (KEY_BRL_DOT2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:499 (KEY_BRL_DOT3) -> linux:499 (KEY_BRL_DOT3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:500 (KEY_BRL_DOT4) -> linux:500 (KEY_BRL_DOT4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:501 (KEY_BRL_DOT5) -> linux:501 (KEY_BRL_DOT5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:502 (KEY_BRL_DOT6) -> linux:502 (KEY_BRL_DOT6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:503 (KEY_BRL_DOT7) -> linux:503 (KEY_BRL_DOT7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:504 (KEY_BRL_DOT8) -> linux:504 (KEY_BRL_DOT8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:505 (KEY_BRL_DOT9) -> linux:505 (KEY_BRL_DOT9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:506 (KEY_BRL_DOT10) -> linux:506 (KEY_BRL_DOT10) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:507 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:508 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:509 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:510 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:511 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:512 (KEY_NUMERIC_0) -> linux:512 (KEY_NUMERIC_0) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:513 (KEY_NUMERIC_1) -> linux:513 (KEY_NUMERIC_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:514 (KEY_NUMERIC_2) -> linux:514 (KEY_NUMERIC_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:515 (KEY_NUMERIC_3) -> linux:515 (KEY_NUMERIC_3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:516 (KEY_NUMERIC_4) -> linux:516 (KEY_NUMERIC_4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:517 (KEY_NUMERIC_5) -> linux:517 (KEY_NUMERIC_5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:518 (KEY_NUMERIC_6) -> linux:518 (KEY_NUMERIC_6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:519 (KEY_NUMERIC_7) -> linux:519 (KEY_NUMERIC_7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:520 (KEY_NUMERIC_8) -> linux:520 (KEY_NUMERIC_8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:521 (KEY_NUMERIC_9) -> linux:521 (KEY_NUMERIC_9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:522 (KEY_NUMERIC_STAR) -> linux:522 (KEY_NUMERIC_STAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:523 (KEY_NUMERIC_POUND) -> linux:523 (KEY_NUMERIC_POUND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* linux:524 (KEY_RFKILL) -> linux:524 (KEY_RFKILL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
};
const size_t qemu_input_map_linux_to_qcode_len = sizeof(qemu_input_map_linux_to_qcode) / sizeof(qemu_input_map_linux_to_qcode[0]);

View File

@ -0,0 +1,3 @@
#include "hid.h"
extern const QKeyCode qemu_input_map_linux_to_qcode[];
extern const size_t qemu_input_map_linux_to_qcode_len;

View File

@ -0,0 +1,198 @@
#include "input-keymap.h"
//TODO how much does std::map kill perf if any?
const std::map<const QKeyCode, unsigned short> qemu_input_map_qcode_to_qnum = {
{Q_KEY_CODE_0, 0xb}, /* qcode:Q_KEY_CODE_0 (0) -> linux:11 (KEY_0) -> qnum:11 */
{Q_KEY_CODE_1, 0x2}, /* qcode:Q_KEY_CODE_1 (1) -> linux:2 (KEY_1) -> qnum:2 */
{Q_KEY_CODE_2, 0x3}, /* qcode:Q_KEY_CODE_2 (2) -> linux:3 (KEY_2) -> qnum:3 */
{Q_KEY_CODE_3, 0x4}, /* qcode:Q_KEY_CODE_3 (3) -> linux:4 (KEY_3) -> qnum:4 */
{Q_KEY_CODE_4, 0x5}, /* qcode:Q_KEY_CODE_4 (4) -> linux:5 (KEY_4) -> qnum:5 */
{Q_KEY_CODE_5, 0x6}, /* qcode:Q_KEY_CODE_5 (5) -> linux:6 (KEY_5) -> qnum:6 */
{Q_KEY_CODE_6, 0x7}, /* qcode:Q_KEY_CODE_6 (6) -> linux:7 (KEY_6) -> qnum:7 */
{Q_KEY_CODE_7, 0x8}, /* qcode:Q_KEY_CODE_7 (7) -> linux:8 (KEY_7) -> qnum:8 */
{Q_KEY_CODE_8, 0x9}, /* qcode:Q_KEY_CODE_8 (8) -> linux:9 (KEY_8) -> qnum:9 */
{Q_KEY_CODE_9, 0xa}, /* qcode:Q_KEY_CODE_9 (9) -> linux:10 (KEY_9) -> qnum:10 */
{Q_KEY_CODE_A, 0x1e}, /* qcode:Q_KEY_CODE_A (a) -> linux:30 (KEY_A) -> qnum:30 */
{Q_KEY_CODE_AC_BACK, 0xea}, /* qcode:Q_KEY_CODE_AC_BACK (ac_back) -> linux:158 (KEY_BACK) -> qnum:234 */
{Q_KEY_CODE_AC_BOOKMARKS, 0xe6}, /* qcode:Q_KEY_CODE_AC_BOOKMARKS (ac_bookmarks) -> linux:156 (KEY_BOOKMARKS) -> qnum:230 */
{Q_KEY_CODE_AC_FORWARD, 0xe9}, /* qcode:Q_KEY_CODE_AC_FORWARD (ac_forward) -> linux:159 (KEY_FORWARD) -> qnum:233 */
{Q_KEY_CODE_AC_HOME, 0xb2}, /* qcode:Q_KEY_CODE_AC_HOME (ac_home) -> linux:172 (KEY_HOMEPAGE) -> qnum:178 */
{Q_KEY_CODE_AC_REFRESH, 0xe7}, /* qcode:Q_KEY_CODE_AC_REFRESH (ac_refresh) -> linux:173 (KEY_REFRESH) -> qnum:231 */
{Q_KEY_CODE_AGAIN, 0x85}, /* qcode:Q_KEY_CODE_AGAIN (again) -> linux:129 (KEY_AGAIN) -> qnum:133 */
{Q_KEY_CODE_ALT, 0x38}, /* qcode:Q_KEY_CODE_ALT (alt) -> linux:56 (KEY_LEFTALT) -> qnum:56 */
{Q_KEY_CODE_ALT_R, 0xb8}, /* qcode:Q_KEY_CODE_ALT_R (alt_r) -> linux:100 (KEY_RIGHTALT) -> qnum:184 */
{Q_KEY_CODE_APOSTROPHE, 0x28}, /* qcode:Q_KEY_CODE_APOSTROPHE (apostrophe) -> linux:40 (KEY_APOSTROPHE) -> qnum:40 */
{Q_KEY_CODE_ASTERISK, 0x37}, /* qcode:Q_KEY_CODE_ASTERISK (kp_multiply) -> linux:55 (KEY_KPASTERISK) -> qnum:55 */
{Q_KEY_CODE_AUDIOMUTE, 0xa0}, /* qcode:Q_KEY_CODE_AUDIOMUTE (audiomute) -> linux:113 (KEY_MUTE) -> qnum:160 */
{Q_KEY_CODE_AUDIONEXT, 0x99}, /* qcode:Q_KEY_CODE_AUDIONEXT (audionext) -> linux:163 (KEY_NEXTSONG) -> qnum:153 */
{Q_KEY_CODE_AUDIOPLAY, 0xa2}, /* qcode:Q_KEY_CODE_AUDIOPLAY (audioplay) -> linux:164 (KEY_PLAYPAUSE) -> qnum:162 */
{Q_KEY_CODE_AUDIOPREV, 0x90}, /* qcode:Q_KEY_CODE_AUDIOPREV (audioprev) -> linux:165 (KEY_PREVIOUSSONG) -> qnum:144 */
{Q_KEY_CODE_AUDIOSTOP, 0xa4}, /* qcode:Q_KEY_CODE_AUDIOSTOP (audiostop) -> linux:166 (KEY_STOPCD) -> qnum:164 */
{Q_KEY_CODE_B, 0x30}, /* qcode:Q_KEY_CODE_B (b) -> linux:48 (KEY_B) -> qnum:48 */
{Q_KEY_CODE_BACKSLASH, 0x2b}, /* qcode:Q_KEY_CODE_BACKSLASH (backslash) -> linux:43 (KEY_BACKSLASH) -> qnum:43 */
{Q_KEY_CODE_BACKSPACE, 0xe}, /* qcode:Q_KEY_CODE_BACKSPACE (backspace) -> linux:14 (KEY_BACKSPACE) -> qnum:14 */
{Q_KEY_CODE_BRACKET_LEFT, 0x1a}, /* qcode:Q_KEY_CODE_BRACKET_LEFT (bracket_left) -> linux:26 (KEY_LEFTBRACE) -> qnum:26 */
{Q_KEY_CODE_BRACKET_RIGHT, 0x1b}, /* qcode:Q_KEY_CODE_BRACKET_RIGHT (bracket_right) -> linux:27 (KEY_RIGHTBRACE) -> qnum:27 */
{Q_KEY_CODE_C, 0x2e}, /* qcode:Q_KEY_CODE_C (c) -> linux:46 (KEY_C) -> qnum:46 */
{Q_KEY_CODE_CALCULATOR, 0xa1}, /* qcode:Q_KEY_CODE_CALCULATOR (calculator) -> linux:140 (KEY_CALC) -> qnum:161 */
{Q_KEY_CODE_CAPS_LOCK, 0x3a}, /* qcode:Q_KEY_CODE_CAPS_LOCK (caps_lock) -> linux:58 (KEY_CAPSLOCK) -> qnum:58 */
{Q_KEY_CODE_COMMA, 0x33}, /* qcode:Q_KEY_CODE_COMMA (comma) -> linux:51 (KEY_COMMA) -> qnum:51 */
{Q_KEY_CODE_COMPOSE, 0xdd}, /* qcode:Q_KEY_CODE_COMPOSE (compose) -> linux:127 (KEY_COMPOSE) -> qnum:221 */
{Q_KEY_CODE_COMPUTER, 0xeb}, /* qcode:Q_KEY_CODE_COMPUTER (computer) -> linux:157 (KEY_COMPUTER) -> qnum:235 */
{Q_KEY_CODE_COPY, 0xf8}, /* qcode:Q_KEY_CODE_COPY (copy) -> linux:133 (KEY_COPY) -> qnum:248 */
{Q_KEY_CODE_CTRL, 0x1d}, /* qcode:Q_KEY_CODE_CTRL (ctrl) -> linux:29 (KEY_LEFTCTRL) -> qnum:29 */
{Q_KEY_CODE_CTRL_R, 0x9d}, /* qcode:Q_KEY_CODE_CTRL_R (ctrl_r) -> linux:97 (KEY_RIGHTCTRL) -> qnum:157 */
{Q_KEY_CODE_CUT, 0xbc}, /* qcode:Q_KEY_CODE_CUT (cut) -> linux:137 (KEY_CUT) -> qnum:188 */
{Q_KEY_CODE_D, 0x20}, /* qcode:Q_KEY_CODE_D (d) -> linux:32 (KEY_D) -> qnum:32 */
{Q_KEY_CODE_DELETE, 0xd3}, /* qcode:Q_KEY_CODE_DELETE (delete) -> linux:111 (KEY_DELETE) -> qnum:211 */
{Q_KEY_CODE_DOT, 0x34}, /* qcode:Q_KEY_CODE_DOT (dot) -> linux:52 (KEY_DOT) -> qnum:52 */
{Q_KEY_CODE_DOWN, 0xd0}, /* qcode:Q_KEY_CODE_DOWN (down) -> linux:108 (KEY_DOWN) -> qnum:208 */
{Q_KEY_CODE_E, 0x12}, /* qcode:Q_KEY_CODE_E (e) -> linux:18 (KEY_E) -> qnum:18 */
{Q_KEY_CODE_END, 0xcf}, /* qcode:Q_KEY_CODE_END (end) -> linux:107 (KEY_END) -> qnum:207 */
{Q_KEY_CODE_EQUAL, 0xd}, /* qcode:Q_KEY_CODE_EQUAL (equal) -> linux:13 (KEY_EQUAL) -> qnum:13 */
{Q_KEY_CODE_ESC, 0x1}, /* qcode:Q_KEY_CODE_ESC (esc) -> linux:1 (KEY_ESC) -> qnum:1 */
{Q_KEY_CODE_F, 0x21}, /* qcode:Q_KEY_CODE_F (f) -> linux:33 (KEY_F) -> qnum:33 */
{Q_KEY_CODE_F1, 0x3b}, /* qcode:Q_KEY_CODE_F1 (f1) -> linux:59 (KEY_F1) -> qnum:59 */
{Q_KEY_CODE_F10, 0x44}, /* qcode:Q_KEY_CODE_F10 (f10) -> linux:68 (KEY_F10) -> qnum:68 */
{Q_KEY_CODE_F11, 0x57}, /* qcode:Q_KEY_CODE_F11 (f11) -> linux:87 (KEY_F11) -> qnum:87 */
{Q_KEY_CODE_F12, 0x58}, /* qcode:Q_KEY_CODE_F12 (f12) -> linux:88 (KEY_F12) -> qnum:88 */
{Q_KEY_CODE_F2, 0x3c}, /* qcode:Q_KEY_CODE_F2 (f2) -> linux:60 (KEY_F2) -> qnum:60 */
{Q_KEY_CODE_F3, 0x3d}, /* qcode:Q_KEY_CODE_F3 (f3) -> linux:61 (KEY_F3) -> qnum:61 */
{Q_KEY_CODE_F4, 0x3e}, /* qcode:Q_KEY_CODE_F4 (f4) -> linux:62 (KEY_F4) -> qnum:62 */
{Q_KEY_CODE_F5, 0x3f}, /* qcode:Q_KEY_CODE_F5 (f5) -> linux:63 (KEY_F5) -> qnum:63 */
{Q_KEY_CODE_F6, 0x40}, /* qcode:Q_KEY_CODE_F6 (f6) -> linux:64 (KEY_F6) -> qnum:64 */
{Q_KEY_CODE_F7, 0x41}, /* qcode:Q_KEY_CODE_F7 (f7) -> linux:65 (KEY_F7) -> qnum:65 */
{Q_KEY_CODE_F8, 0x42}, /* qcode:Q_KEY_CODE_F8 (f8) -> linux:66 (KEY_F8) -> qnum:66 */
{Q_KEY_CODE_F9, 0x43}, /* qcode:Q_KEY_CODE_F9 (f9) -> linux:67 (KEY_F9) -> qnum:67 */
{Q_KEY_CODE_FIND, 0xc1}, /* qcode:Q_KEY_CODE_FIND (find) -> linux:136 (KEY_FIND) -> qnum:193 */
{Q_KEY_CODE_FRONT, 0x8c}, /* qcode:Q_KEY_CODE_FRONT (front) -> linux:132 (KEY_FRONT) -> qnum:140 */
{Q_KEY_CODE_G, 0x22}, /* qcode:Q_KEY_CODE_G (g) -> linux:34 (KEY_G) -> qnum:34 */
{Q_KEY_CODE_GRAVE_ACCENT, 0x29}, /* qcode:Q_KEY_CODE_GRAVE_ACCENT (grave_accent) -> linux:41 (KEY_GRAVE) -> qnum:41 */
{Q_KEY_CODE_H, 0x23}, /* qcode:Q_KEY_CODE_H (h) -> linux:35 (KEY_H) -> qnum:35 */
{Q_KEY_CODE_HELP, 0xf5}, /* qcode:Q_KEY_CODE_HELP (help) -> linux:138 (KEY_HELP) -> qnum:245 */
{Q_KEY_CODE_HENKAN, 0x79}, /* qcode:Q_KEY_CODE_HENKAN (henkan) -> linux:92 (KEY_HENKAN) -> qnum:121 */
{Q_KEY_CODE_HIRAGANA, 0x77}, /* qcode:Q_KEY_CODE_HIRAGANA (hiragana) -> linux:91 (KEY_HIRAGANA) -> qnum:119 */
{Q_KEY_CODE_HOME, 0xc7}, /* qcode:Q_KEY_CODE_HOME (home) -> linux:102 (KEY_HOME) -> qnum:199 */
{Q_KEY_CODE_I, 0x17}, /* qcode:Q_KEY_CODE_I (i) -> linux:23 (KEY_I) -> qnum:23 */
{Q_KEY_CODE_INSERT, 0xd2}, /* qcode:Q_KEY_CODE_INSERT (insert) -> linux:110 (KEY_INSERT) -> qnum:210 */
{Q_KEY_CODE_J, 0x24}, /* qcode:Q_KEY_CODE_J (j) -> linux:36 (KEY_J) -> qnum:36 */
{Q_KEY_CODE_K, 0x25}, /* qcode:Q_KEY_CODE_K (k) -> linux:37 (KEY_K) -> qnum:37 */
{Q_KEY_CODE_KATAKANAHIRAGANA, 0x70}, /* qcode:Q_KEY_CODE_KATAKANAHIRAGANA (katakanahiragana) -> linux:93 (KEY_KATAKANAHIRAGANA) -> qnum:112 */
{Q_KEY_CODE_KP_0, 0x52}, /* qcode:Q_KEY_CODE_KP_0 (kp_0) -> linux:82 (KEY_KP0) -> qnum:82 */
{Q_KEY_CODE_KP_1, 0x4f}, /* qcode:Q_KEY_CODE_KP_1 (kp_1) -> linux:79 (KEY_KP1) -> qnum:79 */
{Q_KEY_CODE_KP_2, 0x50}, /* qcode:Q_KEY_CODE_KP_2 (kp_2) -> linux:80 (KEY_KP2) -> qnum:80 */
{Q_KEY_CODE_KP_3, 0x51}, /* qcode:Q_KEY_CODE_KP_3 (kp_3) -> linux:81 (KEY_KP3) -> qnum:81 */
{Q_KEY_CODE_KP_4, 0x4b}, /* qcode:Q_KEY_CODE_KP_4 (kp_4) -> linux:75 (KEY_KP4) -> qnum:75 */
{Q_KEY_CODE_KP_5, 0x4c}, /* qcode:Q_KEY_CODE_KP_5 (kp_5) -> linux:76 (KEY_KP5) -> qnum:76 */
{Q_KEY_CODE_KP_6, 0x4d}, /* qcode:Q_KEY_CODE_KP_6 (kp_6) -> linux:77 (KEY_KP6) -> qnum:77 */
{Q_KEY_CODE_KP_7, 0x47}, /* qcode:Q_KEY_CODE_KP_7 (kp_7) -> linux:71 (KEY_KP7) -> qnum:71 */
{Q_KEY_CODE_KP_8, 0x48}, /* qcode:Q_KEY_CODE_KP_8 (kp_8) -> linux:72 (KEY_KP8) -> qnum:72 */
{Q_KEY_CODE_KP_9, 0x49}, /* qcode:Q_KEY_CODE_KP_9 (kp_9) -> linux:73 (KEY_KP9) -> qnum:73 */
{Q_KEY_CODE_KP_ADD, 0x4e}, /* qcode:Q_KEY_CODE_KP_ADD (kp_add) -> linux:78 (KEY_KPPLUS) -> qnum:78 */
{Q_KEY_CODE_KP_COMMA, 0x7e}, /* qcode:Q_KEY_CODE_KP_COMMA (kp_comma) -> linux:121 (KEY_KPCOMMA) -> qnum:126 */
{Q_KEY_CODE_KP_DECIMAL, 0x53}, /* qcode:Q_KEY_CODE_KP_DECIMAL (kp_decimal) -> linux:83 (KEY_KPDOT) -> qnum:83 */
{Q_KEY_CODE_KP_DIVIDE, 0xb5}, /* qcode:Q_KEY_CODE_KP_DIVIDE (kp_divide) -> linux:98 (KEY_KPSLASH) -> qnum:181 */
{Q_KEY_CODE_KP_ENTER, 0x9c}, /* qcode:Q_KEY_CODE_KP_ENTER (kp_enter) -> linux:96 (KEY_KPENTER) -> qnum:156 */
{Q_KEY_CODE_KP_EQUALS, 0x59}, /* qcode:Q_KEY_CODE_KP_EQUALS (kp_equals) -> linux:117 (KEY_KPEQUAL) -> qnum:89 */
{Q_KEY_CODE_KP_MULTIPLY, 0x37}, /* qcode:Q_KEY_CODE_KP_MULTIPLY (kp_multiply) -> linux:55 (KEY_KPASTERISK) -> qnum:55 */
{Q_KEY_CODE_KP_SUBTRACT, 0x4a}, /* qcode:Q_KEY_CODE_KP_SUBTRACT (kp_subtract) -> linux:74 (KEY_KPMINUS) -> qnum:74 */
{Q_KEY_CODE_L, 0x26}, /* qcode:Q_KEY_CODE_L (l) -> linux:38 (KEY_L) -> qnum:38 */
{Q_KEY_CODE_LEFT, 0xcb}, /* qcode:Q_KEY_CODE_LEFT (left) -> linux:105 (KEY_LEFT) -> qnum:203 */
{Q_KEY_CODE_LESS, 0x56}, /* qcode:Q_KEY_CODE_LESS (less) -> linux:86 (KEY_102ND) -> qnum:86 */
{Q_KEY_CODE_LF, 0x5b}, /* qcode:Q_KEY_CODE_LF (lf) -> linux:101 (KEY_LINEFEED) -> qnum:91 */
{Q_KEY_CODE_M, 0x32}, /* qcode:Q_KEY_CODE_M (m) -> linux:50 (KEY_M) -> qnum:50 */
{Q_KEY_CODE_MAIL, 0xec}, /* qcode:Q_KEY_CODE_MAIL (mail) -> linux:155 (KEY_MAIL) -> qnum:236 */
{Q_KEY_CODE_MEDIASELECT, 0xed}, /* qcode:Q_KEY_CODE_MEDIASELECT (mediaselect) -> linux:226 (KEY_MEDIA) -> qnum:237 */
{Q_KEY_CODE_MENU, 0x9e}, /* qcode:Q_KEY_CODE_MENU (menu) -> linux:139 (KEY_MENU) -> qnum:158 */
{Q_KEY_CODE_META_L, 0xdb}, /* qcode:Q_KEY_CODE_META_L (meta_l) -> linux:125 (KEY_LEFTMETA) -> qnum:219 */
{Q_KEY_CODE_META_R, 0xdc}, /* qcode:Q_KEY_CODE_META_R (meta_r) -> linux:126 (KEY_RIGHTMETA) -> qnum:220 */
{Q_KEY_CODE_MINUS, 0xc}, /* qcode:Q_KEY_CODE_MINUS (minus) -> linux:12 (KEY_MINUS) -> qnum:12 */
{Q_KEY_CODE_MUHENKAN, 0x7b}, /* qcode:Q_KEY_CODE_MUHENKAN (muhenkan) -> linux:94 (KEY_MUHENKAN) -> qnum:123 */
{Q_KEY_CODE_N, 0x31}, /* qcode:Q_KEY_CODE_N (n) -> linux:49 (KEY_N) -> qnum:49 */
{Q_KEY_CODE_NUM_LOCK, 0x45}, /* qcode:Q_KEY_CODE_NUM_LOCK (num_lock) -> linux:69 (KEY_NUMLOCK) -> qnum:69 */
{Q_KEY_CODE_O, 0x18}, /* qcode:Q_KEY_CODE_O (o) -> linux:24 (KEY_O) -> qnum:24 */
{Q_KEY_CODE_OPEN, 0x64}, /* qcode:Q_KEY_CODE_OPEN (open) -> linux:134 (KEY_OPEN) -> qnum:100 */
{Q_KEY_CODE_P, 0x19}, /* qcode:Q_KEY_CODE_P (p) -> linux:25 (KEY_P) -> qnum:25 */
{Q_KEY_CODE_PASTE, 0x65}, /* qcode:Q_KEY_CODE_PASTE (paste) -> linux:135 (KEY_PASTE) -> qnum:101 */
{Q_KEY_CODE_PAUSE, 0xc6}, /* qcode:Q_KEY_CODE_PAUSE (pause) -> linux:119 (KEY_PAUSE) -> qnum:198 */
{Q_KEY_CODE_PGDN, 0xd1}, /* qcode:Q_KEY_CODE_PGDN (pgdn) -> linux:109 (KEY_PAGEDOWN) -> qnum:209 */
{Q_KEY_CODE_PGUP, 0xc9}, /* qcode:Q_KEY_CODE_PGUP (pgup) -> linux:104 (KEY_PAGEUP) -> qnum:201 */
{Q_KEY_CODE_POWER, 0xde}, /* qcode:Q_KEY_CODE_POWER (power) -> linux:116 (KEY_POWER) -> qnum:222 */
{Q_KEY_CODE_PRINT, 0x54}, /* qcode:Q_KEY_CODE_PRINT (sysrq) -> linux:99 (KEY_SYSRQ) -> qnum:84 */
{Q_KEY_CODE_PROPS, 0x86}, /* qcode:Q_KEY_CODE_PROPS (props) -> linux:130 (KEY_PROPS) -> qnum:134 */
{Q_KEY_CODE_Q, 0x10}, /* qcode:Q_KEY_CODE_Q (q) -> linux:16 (KEY_Q) -> qnum:16 */
{Q_KEY_CODE_R, 0x13}, /* qcode:Q_KEY_CODE_R (r) -> linux:19 (KEY_R) -> qnum:19 */
{Q_KEY_CODE_RET, 0x1c}, /* qcode:Q_KEY_CODE_RET (ret) -> linux:28 (KEY_ENTER) -> qnum:28 */
{Q_KEY_CODE_RIGHT, 0xcd}, /* qcode:Q_KEY_CODE_RIGHT (right) -> linux:106 (KEY_RIGHT) -> qnum:205 */
{Q_KEY_CODE_RO, 0x73}, /* qcode:Q_KEY_CODE_RO (ro) -> linux:89 (KEY_RO) -> qnum:115 */
{Q_KEY_CODE_S, 0x1f}, /* qcode:Q_KEY_CODE_S (s) -> linux:31 (KEY_S) -> qnum:31 */
{Q_KEY_CODE_SCROLL_LOCK, 0x46}, /* qcode:Q_KEY_CODE_SCROLL_LOCK (scroll_lock) -> linux:70 (KEY_SCROLLLOCK) -> qnum:70 */
{Q_KEY_CODE_SEMICOLON, 0x27}, /* qcode:Q_KEY_CODE_SEMICOLON (semicolon) -> linux:39 (KEY_SEMICOLON) -> qnum:39 */
{Q_KEY_CODE_SHIFT, 0x2a}, /* qcode:Q_KEY_CODE_SHIFT (shift) -> linux:42 (KEY_LEFTSHIFT) -> qnum:42 */
{Q_KEY_CODE_SHIFT_R, 0x36}, /* qcode:Q_KEY_CODE_SHIFT_R (shift_r) -> linux:54 (KEY_RIGHTSHIFT) -> qnum:54 */
{Q_KEY_CODE_SLASH, 0x35}, /* qcode:Q_KEY_CODE_SLASH (slash) -> linux:53 (KEY_SLASH) -> qnum:53 */
{Q_KEY_CODE_SLEEP, 0xdf}, /* qcode:Q_KEY_CODE_SLEEP (sleep) -> linux:142 (KEY_SLEEP) -> qnum:223 */
{Q_KEY_CODE_SPC, 0x39}, /* qcode:Q_KEY_CODE_SPC (spc) -> linux:57 (KEY_SPACE) -> qnum:57 */
{Q_KEY_CODE_STOP, 0xe8}, /* qcode:Q_KEY_CODE_STOP (stop) -> linux:128 (KEY_STOP) -> qnum:232 */
{Q_KEY_CODE_SYSRQ, 0x54}, /* qcode:Q_KEY_CODE_SYSRQ (sysrq) -> linux:99 (KEY_SYSRQ) -> qnum:84 */
{Q_KEY_CODE_T, 0x14}, /* qcode:Q_KEY_CODE_T (t) -> linux:20 (KEY_T) -> qnum:20 */
{Q_KEY_CODE_TAB, 0xf}, /* qcode:Q_KEY_CODE_TAB (tab) -> linux:15 (KEY_TAB) -> qnum:15 */
{Q_KEY_CODE_U, 0x16}, /* qcode:Q_KEY_CODE_U (u) -> linux:22 (KEY_U) -> qnum:22 */
{Q_KEY_CODE_UNDO, 0x87}, /* qcode:Q_KEY_CODE_UNDO (undo) -> linux:131 (KEY_UNDO) -> qnum:135 */
{Q_KEY_CODE_UP, 0xc8}, /* qcode:Q_KEY_CODE_UP (up) -> linux:103 (KEY_UP) -> qnum:200 */
{Q_KEY_CODE_V, 0x2f}, /* qcode:Q_KEY_CODE_V (v) -> linux:47 (KEY_V) -> qnum:47 */
{Q_KEY_CODE_VOLUMEDOWN, 0xae}, /* qcode:Q_KEY_CODE_VOLUMEDOWN (volumedown) -> linux:114 (KEY_VOLUMEDOWN) -> qnum:174 */
{Q_KEY_CODE_VOLUMEUP, 0xb0}, /* qcode:Q_KEY_CODE_VOLUMEUP (volumeup) -> linux:115 (KEY_VOLUMEUP) -> qnum:176 */
{Q_KEY_CODE_W, 0x11}, /* qcode:Q_KEY_CODE_W (w) -> linux:17 (KEY_W) -> qnum:17 */
{Q_KEY_CODE_WAKE, 0xe3}, /* qcode:Q_KEY_CODE_WAKE (wake) -> linux:143 (KEY_WAKEUP) -> qnum:227 */
{Q_KEY_CODE_X, 0x2d}, /* qcode:Q_KEY_CODE_X (x) -> linux:45 (KEY_X) -> qnum:45 */
{Q_KEY_CODE_Y, 0x15}, /* qcode:Q_KEY_CODE_Y (y) -> linux:21 (KEY_Y) -> qnum:21 */
{Q_KEY_CODE_YEN, 0x7d}, /* qcode:Q_KEY_CODE_YEN (yen) -> linux:124 (KEY_YEN) -> qnum:125 */
{Q_KEY_CODE_Z, 0x2c}, /* qcode:Q_KEY_CODE_Z (z) -> linux:44 (KEY_Z) -> qnum:44 */
};
int qemu_input_qcode_to_number(const QKeyCode value)
{
auto it = qemu_input_map_qcode_to_qnum.find(value);
if (it == qemu_input_map_qcode_to_qnum.end())
return 0;
return it->second;
}
int qemu_input_key_value_to_number(const KeyValue *value)
{
if (value->type == KEY_VALUE_KIND_QCODE) {
return qemu_input_qcode_to_number(value->u.qcode);
}
else {
assert(value->type == KEY_VALUE_KIND_NUMBER);
return value->u.number;
}
}
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
int *codes)
{
int keycode = qemu_input_key_value_to_number(value);
int count = 0;
if (value->type == KEY_VALUE_KIND_QCODE &&
value->u.qcode == Q_KEY_CODE_PAUSE) {
/* specific case */
int v = down ? 0 : 0x80;
codes[count++] = 0xe1;
codes[count++] = 0x1d | v;
codes[count++] = 0x45 | v;
return count;
}
if (keycode & SCANCODE_GREY) {
codes[count++] = SCANCODE_EMUL0;
keycode &= ~SCANCODE_GREY;
}
if (!down) {
keycode |= SCANCODE_UP;
}
codes[count++] = keycode;
return count;
}

View File

@ -0,0 +1,260 @@
/*
* This file is auto-generated from keymaps.csv on 2018-12-22 15:48
* To re-generate, run:
* keymap-gen --lang=stdc++ --varname=qemu_input_map_win32_to_qcode code-map keymaps.csv win32 qcode
*/
#include "input-keymap-win32-to-qcode.h"
const std::array<QKeyCode, 252> qemu_input_map_win32_to_qcode = {
Q_KEY_CODE_UNMAPPED, /* win32:0 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:1 (VK_LBUTTON) -> linux:256 (BTN_0) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:2 (VK_RBUTTON) -> linux:257 (BTN_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:3 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:4 (VK_MBUTTON) -> linux:258 (BTN_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:5 (VK_XBUTTON1) -> linux:259 (BTN_3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:6 (VK_XBUTTON2) -> linux:260 (BTN_4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:7 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_BACKSPACE, /* win32:8 (VK_BACK) -> linux:14 (KEY_BACKSPACE) -> qcode:Q_KEY_CODE_BACKSPACE (backspace) */
Q_KEY_CODE_TAB, /* win32:9 (VK_TAB) -> linux:15 (KEY_TAB) -> qcode:Q_KEY_CODE_TAB (tab) */
Q_KEY_CODE_UNMAPPED, /* win32:10 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:11 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:12 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_RET, /* win32:13 (VK_RETURN) -> linux:28 (KEY_ENTER) -> qcode:Q_KEY_CODE_RET (ret) */
Q_KEY_CODE_UNMAPPED, /* win32:14 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:15 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_SHIFT, /* win32:16 (VK_LSHIFT) -> linux:42 (KEY_LEFTSHIFT) -> qcode:Q_KEY_CODE_SHIFT (shift) */
Q_KEY_CODE_CTRL, /* win32:17 (VK_CONTROL) -> linux:29 (KEY_LEFTCTRL) -> qcode:Q_KEY_CODE_CTRL (ctrl) */
Q_KEY_CODE_ALT, /* win32:18 (VK_MENU) -> linux:56 (KEY_LEFTALT) -> qcode:Q_KEY_CODE_ALT (alt) */
Q_KEY_CODE_PAUSE, /* win32:19 (VK_PAUSE) -> linux:119 (KEY_PAUSE) -> qcode:Q_KEY_CODE_PAUSE (pause) */
Q_KEY_CODE_CAPS_LOCK, /* win32:20 (VK_CAPITAL) -> linux:58 (KEY_CAPSLOCK) -> qcode:Q_KEY_CODE_CAPS_LOCK (caps_lock) */
Q_KEY_CODE_UNMAPPED, /* win32:21 (VK_HANGEUL) -> linux:122 (KEY_HANGEUL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:22 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:23 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:24 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:25 (VK_HANJA) -> linux:123 (KEY_HANJA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:26 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_ESC, /* win32:27 (VK_ESCAPE) -> linux:1 (KEY_ESC) -> qcode:Q_KEY_CODE_ESC (esc) */
Q_KEY_CODE_UNMAPPED, /* win32:28 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:29 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:30 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:31 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_SPC, /* win32:32 (VK_SPACE) -> linux:57 (KEY_SPACE) -> qcode:Q_KEY_CODE_SPC (spc) */
Q_KEY_CODE_PGUP, /* win32:33 (VK_PRIOR) -> linux:104 (KEY_PAGEUP) -> qcode:Q_KEY_CODE_PGUP (pgup) */
Q_KEY_CODE_PGDN, /* win32:34 (VK_NEXT) -> linux:109 (KEY_PAGEDOWN) -> qcode:Q_KEY_CODE_PGDN (pgdn) */
Q_KEY_CODE_END, /* win32:35 (VK_END) -> linux:107 (KEY_END) -> qcode:Q_KEY_CODE_END (end) */
Q_KEY_CODE_HOME, /* win32:36 (VK_HOME) -> linux:102 (KEY_HOME) -> qcode:Q_KEY_CODE_HOME (home) */
Q_KEY_CODE_LEFT, /* win32:37 (VK_LEFT) -> linux:105 (KEY_LEFT) -> qcode:Q_KEY_CODE_LEFT (left) */
Q_KEY_CODE_UP, /* win32:38 (VK_UP) -> linux:103 (KEY_UP) -> qcode:Q_KEY_CODE_UP (up) */
Q_KEY_CODE_RIGHT, /* win32:39 (VK_RIGHT) -> linux:106 (KEY_RIGHT) -> qcode:Q_KEY_CODE_RIGHT (right) */
Q_KEY_CODE_DOWN, /* win32:40 (VK_DOWN) -> linux:108 (KEY_DOWN) -> qcode:Q_KEY_CODE_DOWN (down) */
Q_KEY_CODE_UNMAPPED, /* win32:41 (VK_SELECT) -> linux:353 (KEY_SELECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:42 (VK_PRINT) -> linux:210 (KEY_PRINT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:43 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_SYSRQ, /* win32:44 (VK_SNAPSHOT) -> linux:99 (KEY_SYSRQ) -> qcode:Q_KEY_CODE_SYSRQ (sysrq) */
Q_KEY_CODE_INSERT, /* win32:45 (VK_INSERT) -> linux:110 (KEY_INSERT) -> qcode:Q_KEY_CODE_INSERT (insert) */
Q_KEY_CODE_DELETE, /* win32:46 (VK_DELETE) -> linux:111 (KEY_DELETE) -> qcode:Q_KEY_CODE_DELETE (delete) */
Q_KEY_CODE_HELP, /* win32:47 (VK_HELP) -> linux:138 (KEY_HELP) -> qcode:Q_KEY_CODE_HELP (help) */
Q_KEY_CODE_0, /* win32:48 (VK_0) -> linux:11 (KEY_0) -> qcode:Q_KEY_CODE_0 (0) */
Q_KEY_CODE_1, /* win32:49 (VK_1) -> linux:2 (KEY_1) -> qcode:Q_KEY_CODE_1 (1) */
Q_KEY_CODE_2, /* win32:50 (VK_2) -> linux:3 (KEY_2) -> qcode:Q_KEY_CODE_2 (2) */
Q_KEY_CODE_3, /* win32:51 (VK_3) -> linux:4 (KEY_3) -> qcode:Q_KEY_CODE_3 (3) */
Q_KEY_CODE_4, /* win32:52 (VK_4) -> linux:5 (KEY_4) -> qcode:Q_KEY_CODE_4 (4) */
Q_KEY_CODE_5, /* win32:53 (VK_5) -> linux:6 (KEY_5) -> qcode:Q_KEY_CODE_5 (5) */
Q_KEY_CODE_6, /* win32:54 (VK_6) -> linux:7 (KEY_6) -> qcode:Q_KEY_CODE_6 (6) */
Q_KEY_CODE_7, /* win32:55 (VK_7) -> linux:8 (KEY_7) -> qcode:Q_KEY_CODE_7 (7) */
Q_KEY_CODE_8, /* win32:56 (VK_8) -> linux:9 (KEY_8) -> qcode:Q_KEY_CODE_8 (8) */
Q_KEY_CODE_9, /* win32:57 (VK_9) -> linux:10 (KEY_9) -> qcode:Q_KEY_CODE_9 (9) */
Q_KEY_CODE_UNMAPPED, /* win32:58 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:59 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:60 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:61 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:62 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:63 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:64 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_A, /* win32:65 (VK_A) -> linux:30 (KEY_A) -> qcode:Q_KEY_CODE_A (a) */
Q_KEY_CODE_B, /* win32:66 (VK_B) -> linux:48 (KEY_B) -> qcode:Q_KEY_CODE_B (b) */
Q_KEY_CODE_C, /* win32:67 (VK_C) -> linux:46 (KEY_C) -> qcode:Q_KEY_CODE_C (c) */
Q_KEY_CODE_D, /* win32:68 (VK_D) -> linux:32 (KEY_D) -> qcode:Q_KEY_CODE_D (d) */
Q_KEY_CODE_E, /* win32:69 (VK_E) -> linux:18 (KEY_E) -> qcode:Q_KEY_CODE_E (e) */
Q_KEY_CODE_F, /* win32:70 (VK_F) -> linux:33 (KEY_F) -> qcode:Q_KEY_CODE_F (f) */
Q_KEY_CODE_G, /* win32:71 (VK_G) -> linux:34 (KEY_G) -> qcode:Q_KEY_CODE_G (g) */
Q_KEY_CODE_H, /* win32:72 (VK_H) -> linux:35 (KEY_H) -> qcode:Q_KEY_CODE_H (h) */
Q_KEY_CODE_I, /* win32:73 (VK_I) -> linux:23 (KEY_I) -> qcode:Q_KEY_CODE_I (i) */
Q_KEY_CODE_J, /* win32:74 (VK_J) -> linux:36 (KEY_J) -> qcode:Q_KEY_CODE_J (j) */
Q_KEY_CODE_K, /* win32:75 (VK_K) -> linux:37 (KEY_K) -> qcode:Q_KEY_CODE_K (k) */
Q_KEY_CODE_L, /* win32:76 (VK_L) -> linux:38 (KEY_L) -> qcode:Q_KEY_CODE_L (l) */
Q_KEY_CODE_M, /* win32:77 (VK_M) -> linux:50 (KEY_M) -> qcode:Q_KEY_CODE_M (m) */
Q_KEY_CODE_N, /* win32:78 (VK_N) -> linux:49 (KEY_N) -> qcode:Q_KEY_CODE_N (n) */
Q_KEY_CODE_O, /* win32:79 (VK_O) -> linux:24 (KEY_O) -> qcode:Q_KEY_CODE_O (o) */
Q_KEY_CODE_P, /* win32:80 (VK_P) -> linux:25 (KEY_P) -> qcode:Q_KEY_CODE_P (p) */
Q_KEY_CODE_Q, /* win32:81 (VK_Q) -> linux:16 (KEY_Q) -> qcode:Q_KEY_CODE_Q (q) */
Q_KEY_CODE_R, /* win32:82 (VK_R) -> linux:19 (KEY_R) -> qcode:Q_KEY_CODE_R (r) */
Q_KEY_CODE_S, /* win32:83 (VK_S) -> linux:31 (KEY_S) -> qcode:Q_KEY_CODE_S (s) */
Q_KEY_CODE_T, /* win32:84 (VK_T) -> linux:20 (KEY_T) -> qcode:Q_KEY_CODE_T (t) */
Q_KEY_CODE_U, /* win32:85 (VK_U) -> linux:22 (KEY_U) -> qcode:Q_KEY_CODE_U (u) */
Q_KEY_CODE_V, /* win32:86 (VK_V) -> linux:47 (KEY_V) -> qcode:Q_KEY_CODE_V (v) */
Q_KEY_CODE_W, /* win32:87 (VK_W) -> linux:17 (KEY_W) -> qcode:Q_KEY_CODE_W (w) */
Q_KEY_CODE_X, /* win32:88 (VK_X) -> linux:45 (KEY_X) -> qcode:Q_KEY_CODE_X (x) */
Q_KEY_CODE_Y, /* win32:89 (VK_Y) -> linux:21 (KEY_Y) -> qcode:Q_KEY_CODE_Y (y) */
Q_KEY_CODE_Z, /* win32:90 (VK_Z) -> linux:44 (KEY_Z) -> qcode:Q_KEY_CODE_Z (z) */
Q_KEY_CODE_META_L, /* win32:91 (VK_LWIN) -> linux:125 (KEY_LEFTMETA) -> qcode:Q_KEY_CODE_META_L (meta_l) */
Q_KEY_CODE_META_R, /* win32:92 (VK_RWIN) -> linux:126 (KEY_RIGHTMETA) -> qcode:Q_KEY_CODE_META_R (meta_r) */
Q_KEY_CODE_COMPOSE, /* win32:93 (VK_APPS) -> linux:127 (KEY_COMPOSE) -> qcode:Q_KEY_CODE_COMPOSE (compose) */
Q_KEY_CODE_UNMAPPED, /* win32:94 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_SLEEP, /* win32:95 (VK_SLEEP) -> linux:142 (KEY_SLEEP) -> qcode:Q_KEY_CODE_SLEEP (sleep) */
Q_KEY_CODE_KP_0, /* win32:96 (VK_NUMPAD0) -> linux:82 (KEY_KP0) -> qcode:Q_KEY_CODE_KP_0 (kp_0) */
Q_KEY_CODE_KP_1, /* win32:97 (VK_NUMPAD1) -> linux:79 (KEY_KP1) -> qcode:Q_KEY_CODE_KP_1 (kp_1) */
Q_KEY_CODE_KP_2, /* win32:98 (VK_NUMPAD2) -> linux:80 (KEY_KP2) -> qcode:Q_KEY_CODE_KP_2 (kp_2) */
Q_KEY_CODE_KP_3, /* win32:99 (VK_NUMPAD3) -> linux:81 (KEY_KP3) -> qcode:Q_KEY_CODE_KP_3 (kp_3) */
Q_KEY_CODE_KP_4, /* win32:100 (VK_NUMPAD4) -> linux:75 (KEY_KP4) -> qcode:Q_KEY_CODE_KP_4 (kp_4) */
Q_KEY_CODE_KP_5, /* win32:101 (VK_NUMPAD5) -> linux:76 (KEY_KP5) -> qcode:Q_KEY_CODE_KP_5 (kp_5) */
Q_KEY_CODE_KP_6, /* win32:102 (VK_NUMPAD6) -> linux:77 (KEY_KP6) -> qcode:Q_KEY_CODE_KP_6 (kp_6) */
Q_KEY_CODE_KP_7, /* win32:103 (VK_NUMPAD7) -> linux:71 (KEY_KP7) -> qcode:Q_KEY_CODE_KP_7 (kp_7) */
Q_KEY_CODE_KP_8, /* win32:104 (VK_NUMPAD8) -> linux:72 (KEY_KP8) -> qcode:Q_KEY_CODE_KP_8 (kp_8) */
Q_KEY_CODE_KP_9, /* win32:105 (VK_NUMPAD9) -> linux:73 (KEY_KP9) -> qcode:Q_KEY_CODE_KP_9 (kp_9) */
Q_KEY_CODE_KP_MULTIPLY, /* win32:106 (VK_MULTIPLY) -> linux:55 (KEY_KPASTERISK) -> qcode:Q_KEY_CODE_KP_MULTIPLY (kp_multiply) */
Q_KEY_CODE_KP_ADD, /* win32:107 (VK_ADD) -> linux:78 (KEY_KPPLUS) -> qcode:Q_KEY_CODE_KP_ADD (kp_add) */
Q_KEY_CODE_KP_COMMA, /* win32:108 (VK_SEPARATOR??) -> linux:121 (KEY_KPCOMMA) -> qcode:Q_KEY_CODE_KP_COMMA (kp_comma) */
Q_KEY_CODE_KP_SUBTRACT, /* win32:109 (VK_SUBTRACT) -> linux:74 (KEY_KPMINUS) -> qcode:Q_KEY_CODE_KP_SUBTRACT (kp_subtract) */
Q_KEY_CODE_KP_DECIMAL, /* win32:110 (VK_DECIMAL) -> linux:83 (KEY_KPDOT) -> qcode:Q_KEY_CODE_KP_DECIMAL (kp_decimal) */
Q_KEY_CODE_KP_DIVIDE, /* win32:111 (VK_DIVIDE) -> linux:98 (KEY_KPSLASH) -> qcode:Q_KEY_CODE_KP_DIVIDE (kp_divide) */
Q_KEY_CODE_F1, /* win32:112 (VK_F1) -> linux:59 (KEY_F1) -> qcode:Q_KEY_CODE_F1 (f1) */
Q_KEY_CODE_F2, /* win32:113 (VK_F2) -> linux:60 (KEY_F2) -> qcode:Q_KEY_CODE_F2 (f2) */
Q_KEY_CODE_F3, /* win32:114 (VK_F3) -> linux:61 (KEY_F3) -> qcode:Q_KEY_CODE_F3 (f3) */
Q_KEY_CODE_F4, /* win32:115 (VK_F4) -> linux:62 (KEY_F4) -> qcode:Q_KEY_CODE_F4 (f4) */
Q_KEY_CODE_F5, /* win32:116 (VK_F5) -> linux:63 (KEY_F5) -> qcode:Q_KEY_CODE_F5 (f5) */
Q_KEY_CODE_F6, /* win32:117 (VK_F6) -> linux:64 (KEY_F6) -> qcode:Q_KEY_CODE_F6 (f6) */
Q_KEY_CODE_F7, /* win32:118 (VK_F7) -> linux:65 (KEY_F7) -> qcode:Q_KEY_CODE_F7 (f7) */
Q_KEY_CODE_F8, /* win32:119 (VK_F8) -> linux:66 (KEY_F8) -> qcode:Q_KEY_CODE_F8 (f8) */
Q_KEY_CODE_F9, /* win32:120 (VK_F9) -> linux:67 (KEY_F9) -> qcode:Q_KEY_CODE_F9 (f9) */
Q_KEY_CODE_F10, /* win32:121 (VK_F10) -> linux:68 (KEY_F10) -> qcode:Q_KEY_CODE_F10 (f10) */
Q_KEY_CODE_F11, /* win32:122 (VK_F11) -> linux:87 (KEY_F11) -> qcode:Q_KEY_CODE_F11 (f11) */
Q_KEY_CODE_F12, /* win32:123 (VK_F12) -> linux:88 (KEY_F12) -> qcode:Q_KEY_CODE_F12 (f12) */
Q_KEY_CODE_UNMAPPED, /* win32:124 (VK_F13) -> linux:183 (KEY_F13) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:125 (VK_F14) -> linux:184 (KEY_F14) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:126 (VK_F15) -> linux:185 (KEY_F15) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:127 (VK_F16) -> linux:186 (KEY_F16) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:128 (VK_F17) -> linux:187 (KEY_F17) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:129 (VK_F18) -> linux:188 (KEY_F18) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:130 (VK_F19) -> linux:189 (KEY_F19) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:131 (VK_F20) -> linux:190 (KEY_F20) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:132 (VK_F21) -> linux:191 (KEY_F21) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:133 (VK_F22) -> linux:192 (KEY_F22) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:134 (VK_F23) -> linux:193 (KEY_F23) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:135 (VK_F24) -> linux:194 (KEY_F24) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:136 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:137 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:138 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:139 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:140 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:141 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:142 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:143 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_NUM_LOCK, /* win32:144 (VK_NUMLOCK) -> linux:69 (KEY_NUMLOCK) -> qcode:Q_KEY_CODE_NUM_LOCK (num_lock) */
Q_KEY_CODE_SCROLL_LOCK, /* win32:145 (VK_SCROLL) -> linux:70 (KEY_SCROLLLOCK) -> qcode:Q_KEY_CODE_SCROLL_LOCK (scroll_lock) */
Q_KEY_CODE_UNMAPPED, /* win32:146 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:147 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:148 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:149 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:150 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:151 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:152 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:153 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:154 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:155 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:156 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:157 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:158 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:159 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_SHIFT, /* win32:160 (VK_LSHIFT) -> linux:42 (KEY_LEFTSHIFT) -> qcode:Q_KEY_CODE_SHIFT (shift) */
Q_KEY_CODE_SHIFT_R, /* win32:161 (VK_RSHIFT) -> linux:54 (KEY_RIGHTSHIFT) -> qcode:Q_KEY_CODE_SHIFT_R (shift_r) */
Q_KEY_CODE_CTRL, /* win32:162 (VK_CONTROL) -> linux:29 (KEY_LEFTCTRL) -> qcode:Q_KEY_CODE_CTRL (ctrl) */
Q_KEY_CODE_CTRL_R, /* win32:163 (VK_RCONTROL) -> linux:97 (KEY_RIGHTCTRL) -> qcode:Q_KEY_CODE_CTRL_R (ctrl_r) */
Q_KEY_CODE_ALT, /* win32:164 (VK_MENU) -> linux:56 (KEY_LEFTALT) -> qcode:Q_KEY_CODE_ALT (alt) */
Q_KEY_CODE_ALT_R, /* win32:165 (VK_RMENU) -> linux:100 (KEY_RIGHTALT) -> qcode:Q_KEY_CODE_ALT_R (alt_r) */
Q_KEY_CODE_AC_BACK, /* win32:166 (VK_BROWSER_BACK) -> linux:158 (KEY_BACK) -> qcode:Q_KEY_CODE_AC_BACK (ac_back) */
Q_KEY_CODE_AC_FORWARD, /* win32:167 (VK_BROWSER_FORWARD) -> linux:159 (KEY_FORWARD) -> qcode:Q_KEY_CODE_AC_FORWARD (ac_forward) */
Q_KEY_CODE_AC_REFRESH, /* win32:168 (VK_BROWSER_REFRESH) -> linux:173 (KEY_REFRESH) -> qcode:Q_KEY_CODE_AC_REFRESH (ac_refresh) */
Q_KEY_CODE_STOP, /* win32:169 (VK_BROWSER_STOP) -> linux:128 (KEY_STOP) -> qcode:Q_KEY_CODE_STOP (stop) */
Q_KEY_CODE_UNMAPPED, /* win32:170 (VK_BROWSER_SEARCH) -> linux:217 (KEY_SEARCH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:171 (VK_BROWSER_FAVOURITES) -> linux:364 (KEY_FAVORITES) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_AC_HOME, /* win32:172 (VK_BROWSER_HOME) -> linux:172 (KEY_HOMEPAGE) -> qcode:Q_KEY_CODE_AC_HOME (ac_home) */
Q_KEY_CODE_AUDIOMUTE, /* win32:173 (VK_VOLUME_MUTE) -> linux:113 (KEY_MUTE) -> qcode:Q_KEY_CODE_AUDIOMUTE (audiomute) */
Q_KEY_CODE_VOLUMEDOWN, /* win32:174 (VK_VOLUME_DOWN) -> linux:114 (KEY_VOLUMEDOWN) -> qcode:Q_KEY_CODE_VOLUMEDOWN (volumedown) */
Q_KEY_CODE_VOLUMEUP, /* win32:175 (VK_VOLUME_UP) -> linux:115 (KEY_VOLUMEUP) -> qcode:Q_KEY_CODE_VOLUMEUP (volumeup) */
Q_KEY_CODE_AUDIONEXT, /* win32:176 (VK_MEDIA_NEXT_TRACK) -> linux:163 (KEY_NEXTSONG) -> qcode:Q_KEY_CODE_AUDIONEXT (audionext) */
Q_KEY_CODE_AUDIOPREV, /* win32:177 (VK_MEDIA_PREV_TRACK) -> linux:165 (KEY_PREVIOUSSONG) -> qcode:Q_KEY_CODE_AUDIOPREV (audioprev) */
Q_KEY_CODE_AUDIOSTOP, /* win32:178 (VK_MEDIA_STOP) -> linux:166 (KEY_STOPCD) -> qcode:Q_KEY_CODE_AUDIOSTOP (audiostop) */
Q_KEY_CODE_AUDIOPLAY, /* win32:179 (VK_MEDIA_PLAY_PAUSE) -> linux:164 (KEY_PLAYPAUSE) -> qcode:Q_KEY_CODE_AUDIOPLAY (audioplay) */
Q_KEY_CODE_UNMAPPED, /* win32:180 (VK_LAUNCH_MAIL) -> linux:215 (KEY_EMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:181 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:182 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:183 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:184 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:185 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_SEMICOLON, /* win32:186 (VK_OEM_1) -> linux:39 (KEY_SEMICOLON) -> qcode:Q_KEY_CODE_SEMICOLON (semicolon) */
Q_KEY_CODE_EQUAL, /* win32:187 (VK_OEM_PLUS) -> linux:13 (KEY_EQUAL) -> qcode:Q_KEY_CODE_EQUAL (equal) */
Q_KEY_CODE_COMMA, /* win32:188 (VK_OEM_COMMA) -> linux:51 (KEY_COMMA) -> qcode:Q_KEY_CODE_COMMA (comma) */
Q_KEY_CODE_MINUS, /* win32:189 (VK_OEM_MINUS) -> linux:12 (KEY_MINUS) -> qcode:Q_KEY_CODE_MINUS (minus) */
Q_KEY_CODE_DOT, /* win32:190 (VK_OEM_PERIOD) -> linux:52 (KEY_DOT) -> qcode:Q_KEY_CODE_DOT (dot) */
Q_KEY_CODE_SLASH, /* win32:191 (VK_OEM_2) -> linux:53 (KEY_SLASH) -> qcode:Q_KEY_CODE_SLASH (slash) */
Q_KEY_CODE_GRAVE_ACCENT, /* win32:192 (VK_OEM_3) -> linux:41 (KEY_GRAVE) -> qcode:Q_KEY_CODE_GRAVE_ACCENT (grave_accent) */
Q_KEY_CODE_UNMAPPED, /* win32:193 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:194 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:195 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:196 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:197 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:198 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:199 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:200 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:201 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:202 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:203 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:204 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:205 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:206 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:207 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:208 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:209 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:210 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:211 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:212 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:213 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:214 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:215 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:216 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:217 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:218 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_BRACKET_LEFT, /* win32:219 (VK_OEM_4) -> linux:26 (KEY_LEFTBRACE) -> qcode:Q_KEY_CODE_BRACKET_LEFT (bracket_left) */
Q_KEY_CODE_BACKSLASH, /* win32:220 (VK_OEM_5) -> linux:43 (KEY_BACKSLASH) -> qcode:Q_KEY_CODE_BACKSLASH (backslash) */
Q_KEY_CODE_BRACKET_RIGHT, /* win32:221 (VK_OEM_6) -> linux:27 (KEY_RIGHTBRACE) -> qcode:Q_KEY_CODE_BRACKET_RIGHT (bracket_right) */
Q_KEY_CODE_APOSTROPHE, /* win32:222 (VK_OEM_7) -> linux:40 (KEY_APOSTROPHE) -> qcode:Q_KEY_CODE_APOSTROPHE (apostrophe) */
Q_KEY_CODE_UNMAPPED, /* win32:223 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:224 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_LESS, /* win32:225 (VK_OEM_102) -> linux:86 (KEY_102ND) -> qcode:Q_KEY_CODE_LESS (less) */
Q_KEY_CODE_UNMAPPED, /* win32:226 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:227 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:228 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:229 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:230 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:231 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:232 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:233 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:234 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:235 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:236 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:237 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:238 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:239 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:240 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:241 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:242 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:243 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:244 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:245 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:246 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:247 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:248 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:249 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:250 (VK_PLAY) -> linux:207 (KEY_PLAY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
Q_KEY_CODE_UNMAPPED, /* win32:251 (VK_ZOOM) -> linux:372 (KEY_ZOOM) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */
};

View File

@ -0,0 +1,9 @@
/*
* This file is auto-generated from keymaps.csv on 2018-12-22 15:48
* Database checksum sha256(ef8f29f4e4294479e2789aa61e410c4b0464d4f0ad16bcc1526086a4f123bc10)
* To re-generate, run:
* keymap-gen --lang=stdc++ --varname=qemu_input_map_win32_to_qcode code-map keymaps.csv win32 qcode
*/
#include <array>
#include "hid.h"
extern const std::array<QKeyCode, 252> qemu_input_map_win32_to_qcode;

View File

@ -0,0 +1,7 @@
#include <map>
#include "hid.h"
extern const std::map<const QKeyCode, unsigned short> qemu_input_map_qcode_to_qnum;
int qemu_input_qcode_to_number(const QKeyCode value);
int qemu_input_key_value_to_number(const KeyValue *value);
int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, int *codes);

392
pcsx2/USB/qemu-usb/iov.cpp Normal file
View File

@ -0,0 +1,392 @@
/*
* Helpers for getting linearized buffers from iov / filling buffers into iovs
*
* Copyright IBM, Corp. 2007, 2008
* Copyright (C) 2010 Red Hat, Inc.
*
* Author(s):
* Anthony Liguori <aliguori@us.ibm.com>
* Amit Shah <amit.shah@redhat.com>
* Michael Tokarev <mjt@tls.msk.ru>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "platcompat.h"
#include "vl.h"
//#include "qemu-common.h"
#include "iov.h"
#include "glib.h"
//#include "qemu/sockets.h"
//#include "qemu/cutils.h"
#include <cassert>
size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt,
size_t offset, const void *buf, size_t bytes)
{
size_t done;
unsigned int i;
for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
if (offset < iov[i].iov_len) {
size_t len = MIN(iov[i].iov_len - offset, bytes - done);
memcpy((char *)iov[i].iov_base + offset, (char *)buf + done, len);
done += len;
offset = 0;
} else {
offset -= iov[i].iov_len;
}
}
assert(offset == 0);
return done;
}
size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt,
size_t offset, void *buf, size_t bytes)
{
size_t done;
unsigned int i;
for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
if (offset < iov[i].iov_len) {
size_t len = MIN(iov[i].iov_len - offset, bytes - done);
memcpy((char *)buf + done, (char *)iov[i].iov_base + offset, len);
done += len;
offset = 0;
} else {
offset -= iov[i].iov_len;
}
}
assert(offset == 0);
return done;
}
size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt,
size_t offset, int fillc, size_t bytes)
{
size_t done;
unsigned int i;
for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
if (offset < iov[i].iov_len) {
size_t len = MIN(iov[i].iov_len - offset, bytes - done);
memset((char *)iov[i].iov_base + offset, fillc, len);
done += len;
offset = 0;
} else {
offset -= iov[i].iov_len;
}
}
assert(offset == 0);
return done;
}
size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
{
size_t len;
unsigned int i;
len = 0;
for (i = 0; i < iov_cnt; i++) {
len += iov[i].iov_len;
}
return len;
}
unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt,
const struct iovec *iov, unsigned int iov_cnt,
size_t offset, size_t bytes)
{
size_t len;
unsigned int i, j;
for (i = 0, j = 0;
i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) {
if (offset >= iov[i].iov_len) {
offset -= iov[i].iov_len;
continue;
}
len = MIN(bytes, iov[i].iov_len - offset);
dst_iov[j].iov_base = (char *)iov[i].iov_base + offset;
dst_iov[j].iov_len = len;
j++;
bytes -= len;
offset = 0;
}
assert(offset == 0);
return j;
}
/* io vectors */
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
{
qiov->iov = my_g_new(struct iovec, alloc_hint);
qiov->niov = 0;
qiov->nalloc = alloc_hint;
qiov->size = 0;
}
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov)
{
int i;
qiov->iov = iov;
qiov->niov = niov;
qiov->nalloc = -1;
qiov->size = 0;
for (i = 0; i < niov; i++)
qiov->size += iov[i].iov_len;
}
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
{
assert(qiov->nalloc != -1);
if (qiov->niov == qiov->nalloc) {
qiov->nalloc = 2 * qiov->nalloc + 1;
qiov->iov = my_g_renew(struct iovec, qiov->iov, qiov->nalloc);
}
qiov->iov[qiov->niov].iov_base = base;
qiov->iov[qiov->niov].iov_len = len;
qiov->size += len;
++qiov->niov;
}
/*
* Concatenates (partial) iovecs from src_iov to the end of dst.
* It starts copying after skipping `soffset' bytes at the
* beginning of src and adds individual vectors from src to
* dst copies up to `sbytes' bytes total, or up to the end
* of src_iov if it comes first. This way, it is okay to specify
* very large value for `sbytes' to indicate "up to the end
* of src".
* Only vector pointers are processed, not the actual data buffers.
*/
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
struct iovec *src_iov, unsigned int src_cnt,
size_t soffset, size_t sbytes)
{
unsigned int i;
size_t done;
if (!sbytes) {
return 0;
}
assert(dst->nalloc != -1);
for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) {
if (soffset < src_iov[i].iov_len) {
size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done);
qemu_iovec_add(dst, (char *)src_iov[i].iov_base + soffset, len);
done += len;
soffset = 0;
} else {
soffset -= src_iov[i].iov_len;
}
}
assert(soffset == 0); /* offset beyond end of src */
return done;
}
/*
* Concatenates (partial) iovecs from src to the end of dst.
* It starts copying after skipping `soffset' bytes at the
* beginning of src and adds individual vectors from src to
* dst copies up to `sbytes' bytes total, or up to the end
* of src if it comes first. This way, it is okay to specify
* very large value for `sbytes' to indicate "up to the end
* of src".
* Only vector pointers are processed, not the actual data buffers.
*/
void qemu_iovec_concat(QEMUIOVector *dst,
QEMUIOVector *src, size_t soffset, size_t sbytes)
{
qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes);
}
/*
* Check if the contents of the iovecs are all zero
*/
/*bool qemu_iovec_is_zero(QEMUIOVector *qiov)
{
int i;
for (i = 0; i < qiov->niov; i++) {
size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long));
uint8_t *ptr = (uint8_t *)qiov->iov[i].iov_base;
if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) {
return false;
}
for (; offs < qiov->iov[i].iov_len; offs++) {
if (ptr[offs]) {
return false;
}
}
}
return true;
}*/
void qemu_iovec_destroy(QEMUIOVector *qiov)
{
assert(qiov->nalloc != -1);
qemu_iovec_reset(qiov);
my_g_free(qiov->iov);
qiov->nalloc = 0;
qiov->iov = NULL;
}
void qemu_iovec_reset(QEMUIOVector *qiov)
{
assert(qiov->nalloc != -1);
qiov->niov = 0;
qiov->size = 0;
}
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
void *buf, size_t bytes)
{
return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes);
}
size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset,
const void *buf, size_t bytes)
{
return iov_from_buf(qiov->iov, qiov->niov, offset, buf, bytes);
}
size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset,
int fillc, size_t bytes)
{
return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes);
}
/**
* Check that I/O vector contents are identical
*
* The IO vectors must have the same structure (same length of all parts).
* A typical usage is to compare vectors created with qemu_iovec_clone().
*
* @a: I/O vector
* @b: I/O vector
* @ret: Offset to first mismatching byte or -1 if match
*/
ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
{
int i;
ssize_t offset = 0;
assert(a->niov == b->niov);
for (i = 0; i < a->niov; i++) {
size_t len = 0;
uint8_t *p = (uint8_t *)a->iov[i].iov_base;
uint8_t *q = (uint8_t *)b->iov[i].iov_base;
assert(a->iov[i].iov_len == b->iov[i].iov_len);
while (len < a->iov[i].iov_len && *p++ == *q++) {
len++;
}
offset += len;
if (len != a->iov[i].iov_len) {
return offset;
}
}
return -1;
}
typedef struct {
int src_index;
struct iovec *src_iov;
void *dest_base;
} IOVectorSortElem;
static int sortelem_cmp_src_base(const void *a, const void *b)
{
const IOVectorSortElem *elem_a = (const IOVectorSortElem *)a;
const IOVectorSortElem *elem_b = (const IOVectorSortElem *)b;
/* Don't overflow */
if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
return -1;
} else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
return 1;
} else {
return 0;
}
}
static int sortelem_cmp_src_index(const void *a, const void *b)
{
const IOVectorSortElem *elem_a = (const IOVectorSortElem *)a;
const IOVectorSortElem *elem_b = (const IOVectorSortElem *)b;
return elem_a->src_index - elem_b->src_index;
}
size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
size_t bytes)
{
size_t total = 0;
struct iovec *cur;
for (cur = *iov; *iov_cnt > 0; cur++) {
if (cur->iov_len > bytes) {
cur->iov_base = (uint8_t *)cur->iov_base + bytes;
cur->iov_len -= bytes;
total += bytes;
break;
}
bytes -= cur->iov_len;
total += cur->iov_len;
*iov_cnt -= 1;
}
*iov = cur;
return total;
}
size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
size_t bytes)
{
size_t total = 0;
struct iovec *cur;
if (*iov_cnt == 0) {
return 0;
}
cur = iov + (*iov_cnt - 1);
while (*iov_cnt > 0) {
if (cur->iov_len > bytes) {
cur->iov_len -= bytes;
total += bytes;
break;
}
bytes -= cur->iov_len;
total += cur->iov_len;
cur--;
*iov_cnt -= 1;
}
return total;
}
void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes)
{
size_t total;
unsigned int niov = qiov->niov;
assert(qiov->size >= bytes);
total = iov_discard_back(qiov->iov, &niov, bytes);
assert(total == bytes);
qiov->niov = niov;
qiov->size -= bytes;
}

175
pcsx2/USB/qemu-usb/iov.h Normal file
View File

@ -0,0 +1,175 @@
/*
* Helpers for using (partial) iovecs.
*
* Copyright (C) 2010 Red Hat, Inc.
*
* Author(s):
* Amit Shah <amit.shah@redhat.com>
* Michael Tokarev <mjt@tls.msk.ru>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#ifndef IOV_H
#define IOV_H
#include "platcompat.h" /* ssize_t */
#if !defined(_BITS_UIO_H) && !defined(__iovec_defined) /* /usr/include/bits/uio.h */
struct iovec {
void *iov_base;
size_t iov_len;
};
#endif
/**
* count and return data size, in bytes, of an iovec
* starting at `iov' of `iov_cnt' number of elements.
*/
size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt);
/**
* Copy from single continuous buffer to scatter-gather vector of buffers
* (iovec) and back like memcpy() between two continuous memory regions.
* Data in single continuous buffer starting at address `buf' and
* `bytes' bytes long will be copied to/from an iovec `iov' with
* `iov_cnt' number of elements, starting at byte position `offset'
* within the iovec. If the iovec does not contain enough space,
* only part of data will be copied, up to the end of the iovec.
* Number of bytes actually copied will be returned, which is
* min(bytes, iov_size(iov)-offset)
* `Offset' must point to the inside of iovec.
* It is okay to use very large value for `bytes' since we're
* limited by the size of the iovec anyway, provided that the
* buffer pointed to by buf has enough space. One possible
* such "large" value is -1 (sinice size_t is unsigned),
* so specifying `-1' as `bytes' means 'up to the end of iovec'.
*/
size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt,
size_t offset, const void *buf, size_t bytes);
size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt,
size_t offset, void *buf, size_t bytes);
static inline size_t
iov_from_buf(const struct iovec *iov, unsigned int iov_cnt,
size_t offset, const void *buf, size_t bytes)
{
if (__builtin_constant_p(bytes) && iov_cnt &&
offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) {
memcpy((char *)iov[0].iov_base + offset, buf, bytes);
return bytes;
} else {
return iov_from_buf_full(iov, iov_cnt, offset, buf, bytes);
}
}
static inline size_t
iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
size_t offset, void *buf, size_t bytes)
{
if (__builtin_constant_p(bytes) && iov_cnt &&
offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) {
memcpy(buf, (char *)iov[0].iov_base + offset, bytes);
return bytes;
} else {
return iov_to_buf_full(iov, iov_cnt, offset, buf, bytes);
}
}
/**
* Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements,
* starting at byte offset `start', to value `fillc', repeating it
* `bytes' number of times. `Offset' must point to the inside of iovec.
* If `bytes' is large enough, only last bytes portion of iovec,
* up to the end of it, will be filled with the specified value.
* Function return actual number of bytes processed, which is
* min(size, iov_size(iov) - offset).
* Again, it is okay to use large value for `bytes' to mean "up to the end".
*/
size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt,
size_t offset, int fillc, size_t bytes);
/*
* Send/recv data from/to iovec buffers directly
*
* `offset' bytes in the beginning of iovec buffer are skipped and
* next `bytes' bytes are used, which must be within data of iovec.
*
* r = iov_send_recv(sockfd, iov, iovcnt, offset, bytes, true);
*
* is logically equivalent to
*
* char *buf = malloc(bytes);
* iov_to_buf(iov, iovcnt, offset, buf, bytes);
* r = send(sockfd, buf, bytes, 0);
* free(buf);
*
* For iov_send_recv() _whole_ area being sent or received
* should be within the iovec, not only beginning of it.
*/
ssize_t iov_send_recv(int sockfd, const struct iovec *iov, unsigned iov_cnt,
size_t offset, size_t bytes, bool do_send);
#define iov_recv(sockfd, iov, iov_cnt, offset, bytes) \
iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, false)
#define iov_send(sockfd, iov, iov_cnt, offset, bytes) \
iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, true)
/**
* Produce a text hexdump of iovec `iov' with `iov_cnt' number of elements
* in file `fp', prefixing each line with `prefix' and processing not more
* than `limit' data bytes.
*/
void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt,
FILE *fp, const char *prefix, size_t limit);
/*
* Partial copy of vector from iov to dst_iov (data is not copied).
* dst_iov overlaps iov at a specified offset.
* size of dst_iov is at most bytes. dst vector count is returned.
*/
unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt,
const struct iovec *iov, unsigned int iov_cnt,
size_t offset, size_t bytes);
/*
* Remove a given number of bytes from the front or back of a vector.
* This may update iov and/or iov_cnt to exclude iovec elements that are
* no longer required.
*
* The number of bytes actually discarded is returned. This number may be
* smaller than requested if the vector is too small.
*/
size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
size_t bytes);
size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
size_t bytes);
typedef struct QEMUIOVector {
struct iovec *iov;
int niov;
int nalloc;
size_t size;
} QEMUIOVector;
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
void qemu_iovec_concat(QEMUIOVector *dst,
QEMUIOVector *src, size_t soffset, size_t sbytes);
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
struct iovec *src_iov, unsigned int src_cnt,
size_t soffset, size_t sbytes);
bool qemu_iovec_is_zero(QEMUIOVector *qiov);
void qemu_iovec_destroy(QEMUIOVector *qiov);
void qemu_iovec_reset(QEMUIOVector *qiov);
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
void *buf, size_t bytes);
size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset,
const void *buf, size_t bytes);
size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset,
int fillc, size_t bytes);
ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b);
void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf);
void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes);
#endif

501
pcsx2/USB/qemu-usb/queue.h Normal file
View File

@ -0,0 +1,501 @@
/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */
/*
* QEMU version: Copy from netbsd, removed debug code, removed some of
* the implementations. Left in singly-linked lists, lists, simple
* queues, and tail queues.
*/
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef QEMU_SYS_QUEUE_H
#define QEMU_SYS_QUEUE_H
/*
* This file defines four types of data structures: singly-linked lists,
* lists, simple queues, and tail queues.
*
* A singly-linked list is headed by a single forward pointer. The
* elements are singly linked for minimum space and pointer manipulation
* overhead at the expense of O(n) removal for arbitrary elements. New
* elements can be added to the list after an existing element or at the
* head of the list. Elements being removed from the head of the list
* should use the explicit macro for this purpose for optimum
* efficiency. A singly-linked list may only be traversed in the forward
* direction. Singly-linked lists are ideal for applications with large
* datasets and few or no removals or for implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
//#include "qemu/atomic.h" /* for smp_wmb() */
/*
* List definitions.
*/
#define QLIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define QLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define QLIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#define QLIST_INIT(head) do { \
(head)->lh_first = NULL; \
} while (/*CONSTCOND*/0)
#define QLIST_SWAP(dstlist, srclist, field) do { \
void *tmplist; \
tmplist = (srclist)->lh_first; \
(srclist)->lh_first = (dstlist)->lh_first; \
if ((srclist)->lh_first != NULL) { \
(srclist)->lh_first->field.le_prev = &(srclist)->lh_first; \
} \
(dstlist)->lh_first = tmplist; \
if ((dstlist)->lh_first != NULL) { \
(dstlist)->lh_first->field.le_prev = &(dstlist)->lh_first; \
} \
} while (/*CONSTCOND*/0)
#define QLIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (/*CONSTCOND*/0)
#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (/*CONSTCOND*/0)
#define QLIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (/*CONSTCOND*/0)
#define QLIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
} while (/*CONSTCOND*/0)
#define QLIST_FOREACH(var, head, field) \
for ((var) = ((head)->lh_first); \
(var); \
(var) = ((var)->field.le_next))
#define QLIST_FOREACH_SAFE(var, head, field, next_var) \
for ((var) = ((head)->lh_first); \
(var) && ((next_var) = ((var)->field.le_next), 1); \
(var) = (next_var))
/*
* List access methods.
*/
#define QLIST_EMPTY(head) ((head)->lh_first == NULL)
#define QLIST_FIRST(head) ((head)->lh_first)
#define QLIST_NEXT(elm, field) ((elm)->field.le_next)
/*
* Singly-linked List definitions.
*/
#define QSLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define QSLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define QSLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List functions.
*/
#define QSLIST_INIT(head) do { \
(head)->slh_first = NULL; \
} while (/*CONSTCOND*/0)
#define QSLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (/*CONSTCOND*/0)
#define QSLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (/*CONSTCOND*/0)
#define QSLIST_INSERT_HEAD_ATOMIC(head, elm, field) do { \
typeof(elm) save_sle_next; \
do { \
save_sle_next = (elm)->field.sle_next = (head)->slh_first; \
} while (atomic_cmpxchg(&(head)->slh_first, save_sle_next, (elm)) != \
save_sle_next); \
} while (/*CONSTCOND*/0)
#define QSLIST_MOVE_ATOMIC(dest, src) do { \
(dest)->slh_first = atomic_xchg(&(src)->slh_first, NULL); \
} while (/*CONSTCOND*/0)
#define QSLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (/*CONSTCOND*/0)
#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
(slistelm)->field.sle_next = \
QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
} while (/*CONSTCOND*/0)
#define QSLIST_FOREACH(var, head, field) \
for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
#define QSLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = QSLIST_FIRST((head)); \
(var) && ((tvar) = QSLIST_NEXT((var), field), 1); \
(var) = (tvar))
/*
* Singly-linked List access methods.
*/
#define QSLIST_EMPTY(head) ((head)->slh_first == NULL)
#define QSLIST_FIRST(head) ((head)->slh_first)
#define QSLIST_NEXT(elm, field) ((elm)->field.sle_next)
/*
* Simple queue definitions.
*/
#define QSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define QSIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define QSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue functions.
*/
#define QSIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\
(head)->sqh_last = &(head)->sqh_first; \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \
QSIMPLEQ_INIT(removed); \
if (((removed)->sqh_first = (head)->sqh_first) != NULL) { \
if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) { \
(head)->sqh_last = &(head)->sqh_first; \
} \
(removed)->sqh_last = &(elm)->field.sqe_next; \
(elm)->field.sqe_next = NULL; \
} \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \
if ((head)->sqh_first == (elm)) { \
QSIMPLEQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->sqh_first; \
while (curelm->field.sqe_next != (elm)) \
curelm = curelm->field.sqe_next; \
if ((curelm->field.sqe_next = \
curelm->field.sqe_next->field.sqe_next) == NULL) \
(head)->sqh_last = &(curelm)->field.sqe_next; \
} \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_FOREACH(var, head, field) \
for ((var) = ((head)->sqh_first); \
(var); \
(var) = ((var)->field.sqe_next))
#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \
for ((var) = ((head)->sqh_first); \
(var) && ((next = ((var)->field.sqe_next)), 1); \
(var) = (next))
#define QSIMPLEQ_CONCAT(head1, head2) do { \
if (!QSIMPLEQ_EMPTY((head2))) { \
*(head1)->sqh_last = (head2)->sqh_first; \
(head1)->sqh_last = (head2)->sqh_last; \
QSIMPLEQ_INIT((head2)); \
} \
} while (/*CONSTCOND*/0)
#define QSIMPLEQ_LAST(head, type, field) \
(QSIMPLEQ_EMPTY((head)) ? \
NULL : \
((struct type *)(void *) \
((char *)((head)->sqh_last) - offsetof(struct type, field))))
/*
* Simple queue access methods.
*/
#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
#define QSIMPLEQ_FIRST(head) ((head)->sqh_first)
#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
/*
* Tail queue definitions.
*/
#define Q_TAILQ_HEAD(name, type, qual) \
struct name { \
qual type *tqh_first; /* first element */ \
qual type *qual *tqh_last; /* addr of last next element */ \
}
#define QTAILQ_HEAD(name, type) Q_TAILQ_HEAD(name, struct type,)
#define QTAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define Q_TAILQ_ENTRY(type, qual) \
struct { \
qual type *tqe_next; /* next element */ \
qual type *qual *tqe_prev; /* address of previous next element */\
}
#define QTAILQ_ENTRY(type) Q_TAILQ_ENTRY(struct type,)
/*
* Tail queue functions.
*/
#define QTAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (/*CONSTCOND*/0)
#define QTAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
(elm)->field.tqe_prev = NULL; \
} while (/*CONSTCOND*/0)
#define QTAILQ_FOREACH(var, head, field) \
for ((var) = ((head)->tqh_first); \
(var); \
(var) = ((var)->field.tqe_next))
#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \
for ((var) = ((head)->tqh_first); \
(var) && ((next_var) = ((var)->field.tqe_next), 1); \
(var) = (next_var))
#define QTAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
(var); \
(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
/*
* Tail queue access methods.
*/
#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define QTAILQ_FIRST(head) ((head)->tqh_first)
#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define QTAILQ_IN_USE(elm, field) ((elm)->field.tqe_prev != NULL)
#define QTAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define QTAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define field_at_offset(base, offset, type) \
((type) (((char *) (base)) + (offset)))
typedef struct DUMMY_Q_ENTRY DUMMY_Q_ENTRY;
typedef struct DUMMY_Q DUMMY_Q;
struct DUMMY_Q_ENTRY {
QTAILQ_ENTRY(DUMMY_Q_ENTRY) next;
};
struct DUMMY_Q {
QTAILQ_HEAD(DUMMY_Q_HEAD, DUMMY_Q_ENTRY) head;
};
#define dummy_q ((DUMMY_Q *) 0)
#define dummy_qe ((DUMMY_Q_ENTRY *) 0)
/*
* Offsets of layout of a tail queue head.
*/
#define QTAILQ_FIRST_OFFSET (offsetof(typeof(dummy_q->head), tqh_first))
#define QTAILQ_LAST_OFFSET (offsetof(typeof(dummy_q->head), tqh_last))
/*
* Raw access of elements of a tail queue
*/
#define QTAILQ_RAW_FIRST(head) \
(*field_at_offset(head, QTAILQ_FIRST_OFFSET, void **))
#define QTAILQ_RAW_TQH_LAST(head) \
(*field_at_offset(head, QTAILQ_LAST_OFFSET, void ***))
/*
* Offsets of layout of a tail queue element.
*/
#define QTAILQ_NEXT_OFFSET (offsetof(typeof(dummy_qe->next), tqe_next))
#define QTAILQ_PREV_OFFSET (offsetof(typeof(dummy_qe->next), tqe_prev))
/*
* Raw access of elements of a tail entry
*/
#define QTAILQ_RAW_NEXT(elm, entry) \
(*field_at_offset(elm, entry + QTAILQ_NEXT_OFFSET, void **))
#define QTAILQ_RAW_TQE_PREV(elm, entry) \
(*field_at_offset(elm, entry + QTAILQ_PREV_OFFSET, void ***))
/*
* Tail queue tranversal using pointer arithmetic.
*/
#define QTAILQ_RAW_FOREACH(elm, head, entry) \
for ((elm) = QTAILQ_RAW_FIRST(head); \
(elm); \
(elm) = QTAILQ_RAW_NEXT(elm, entry))
/*
* Tail queue insertion using pointer arithmetic.
*/
#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do { \
QTAILQ_RAW_NEXT(elm, entry) = NULL; \
QTAILQ_RAW_TQE_PREV(elm, entry) = QTAILQ_RAW_TQH_LAST(head); \
*QTAILQ_RAW_TQH_LAST(head) = (elm); \
QTAILQ_RAW_TQH_LAST(head) = &QTAILQ_RAW_NEXT(elm, entry); \
} while (/*CONSTCOND*/0)
#endif /* QEMU_SYS_QUEUE_H */

603
pcsx2/USB/qemu-usb/qusb.h Normal file
View File

@ -0,0 +1,603 @@
/*
* 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.
*/
#include "iov.h"
#include "queue.h"
#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_SUCCESS (0)
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2)
#define USB_RET_STALL (-3)
#define USB_RET_BABBLE (-4)
#define USB_RET_IOERROR (-5)
#define USB_RET_ASYNC (-6)
#define USB_RET_ADD_TO_QUEUE (-7)
#define USB_RET_REMOVE_FROM_QUEUE (-8)
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
#define USB_SPEED_HIGH 2
#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW)
#define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL)
#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_RESERVED 0
#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) //0x8000
#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) // 0x0000
#define VendorDeviceRequest ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) // 0xC000
#define VendorDeviceOutRequest \
((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) //0x4000
#define InterfaceRequest \
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) // 0x8100
#define InterfaceOutRequest \
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) //0x0100
#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) //0x8200
#define EndpointOutRequest \
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) //0x0200
#define ClassInterfaceRequest \
((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) //0xA100
#define ClassInterfaceOutRequest \
((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) //0x2100
#define ClassEndpointRequest \
((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_ENDPOINT)<<8) //0xA200
#define ClassEndpointOutRequest \
((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_ENDPOINT)<<8) //0x2200
#define VendorInterfaceRequest \
((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8)
#define VendorInterfaceOutRequest \
((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<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_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_DEBUG 0x0A
#define USB_DT_INTERFACE_ASSOC 0x0B
#define USB_DT_BOS 0x0F
#define USB_DT_DEVICE_CAPABILITY 0x10
#define USB_DT_HID 0x21
#define USB_DT_REPORT 0x22
#define USB_DT_PHYSICAL 0x23
#define USB_DT_CS_INTERFACE 0x24
#define USB_DT_CS_ENDPOINT 0x25
#define USB_DT_ENDPOINT_COMPANION 0x30
#define USB_DEV_CAP_WIRELESS 0x01
#define USB_DEV_CAP_USB2_EXT 0x02
#define USB_DEV_CAP_SUPERSPEED 0x03
#define USB_CFG_ATT_ONE (1 << 7) /* should always be set */
#define USB_CFG_ATT_SELFPOWER (1 << 6)
#define USB_CFG_ATT_WAKEUP (1 << 5)
#define USB_CFG_ATT_BATTERY (1 << 4)
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_XFER_INVALID 255
#define USB_INTERFACE_INVALID 255
/* HID interface requests */
#define GET_REPORT 0xa101
#define GET_IDLE 0xa102
#define GET_PROTOCOL 0xa103
#define SET_REPORT 0x2109
#define SET_IDLE 0x210a
#define SET_PROTOCOL 0x210b
#define USB_DEVICE_DESC_SIZE 18
#define USB_CONFIGURATION_DESC_SIZE 9
#define USB_INTERFACE_DESC_SIZE 9
#define USB_ENDPOINT_DESC_SIZE 7
#define WBVAL(x) ((x) & 0xFF),(((x) >> 8) & 0xFF)
#define B3VAL(x) ((x) & 0xFF),(((x) >> 8) & 0xFF),(((x) >> 16) & 0xFF)
/* 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
/* 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 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
/* 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
typedef struct USBBus USBBus;
typedef struct USBBusOps USBBusOps;
typedef struct USBPort USBPort;
typedef struct USBDevice USBDevice;
typedef struct USBPacket USBPacket;
typedef struct USBCombinedPacket USBCombinedPacket;
typedef struct USBEndpoint USBEndpoint;
typedef struct USBDesc USBDesc;
typedef struct USBDescID USBDescID;
typedef struct USBDescDevice USBDescDevice;
typedef struct USBDescConfig USBDescConfig;
typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
typedef struct USBDescIface USBDescIface;
typedef struct USBDescEndpoint USBDescEndpoint;
typedef struct USBDescOther USBDescOther;
typedef struct USBDescString USBDescString;
typedef struct USBDescMSOS USBDescMSOS;
struct USBDescString {
uint8_t index;
char *str;
QLIST_ENTRY(USBDescString) next;
};
#define USB_MAX_ENDPOINTS 15
#define USB_MAX_INTERFACES 16
struct USBEndpoint {
uint8_t nr;
uint8_t pid;
uint8_t type;
uint8_t ifnum;
int max_packet_size;
int max_streams;
bool pipeline;
bool halted;
USBDevice *dev;
QTAILQ_HEAD(, USBPacket) queue;
};
enum USBDeviceFlags {
USB_DEV_FLAG_FULL_PATH,
USB_DEV_FLAG_IS_HOST,
USB_DEV_FLAG_MSOS_DESC_ENABLE,
USB_DEV_FLAG_MSOS_DESC_IN_USE,
};
typedef void (*USBDeviceRealize)(USBDevice *dev/*, Error **errp*/);
typedef void (*USBDeviceUnrealize)(USBDevice *dev/*, Error **errp*/);
typedef struct USBDeviceClass {
//DeviceClass parent_class;
USBDeviceRealize realize;
USBDeviceUnrealize unrealize;
/*
* Walk (enabled) downstream ports, check for a matching device.
* Only hubs implement this.
*/
USBDevice *(*find_device)(USBDevice *dev, uint8_t addr);
/*
* Called when a packet is canceled.
*/
void (*cancel_packet)(USBDevice *dev, USBPacket *p);
/*
* Attach the device
*/
void (*handle_attach)(USBDevice *dev);
/*
* Reset the device
*/
void (*handle_reset)(USBDevice *dev);
/*
* Process control request.
* Called from handle_packet().
*
* Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
* then the number of bytes transferred is stored in p->actual_length
*/
void (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
int index, int length, uint8_t *data);
/*
* Process data transfers (both BULK and ISOC).
* Called from handle_packet().
*
* Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
* then the number of bytes transferred is stored in p->actual_length
*/
void (*handle_data)(USBDevice *dev, USBPacket *p);
void (*set_interface)(USBDevice *dev, int intf,
int alt_old, int alt_new);
/*
* Called when the hcd is done queuing packets for an endpoint, only
* necessary for devices which can return USB_RET_ADD_TO_QUEUE.
*/
void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep);
/*
* Called by the hcd to let the device know the queue for an endpoint
* has been unlinked / stopped. Optional may be NULL.
*/
void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep);
/*
* Called by the hcd to alloc / free streams on a bulk endpoint.
* Optional may be NULL.
*/
int (*alloc_streams)(USBDevice *dev, USBEndpoint **eps, int nr_eps,
int streams);
void (*free_streams)(USBDevice *dev, USBEndpoint **eps, int nr_eps);
int (*open)(USBDevice *dev);
void (*close)(USBDevice *dev);
const char *product_desc;
const USBDesc *usb_desc;
bool attached_settable;
} USBDeviceClass;
/* definition of a USB device */
struct USBDevice {
USBDeviceClass klass;
USBPort *port;
USBBus *bus;
void *opaque;
uint32_t flags;
/* Actual connected speed */
int speed;
/* Supported speeds, not in info because it may be variable (hostdevs) */
int speedmask;
uint8_t addr;
char product_desc[32];
int auto_attach;
bool attached;
int32_t state;
uint8_t setup_buf[8];
uint8_t data_buf[4096];
int32_t remote_wakeup;
int32_t setup_state;
int32_t setup_len;
int32_t setup_index;
USBEndpoint ep_ctl;
USBEndpoint ep_in[USB_MAX_ENDPOINTS];
USBEndpoint ep_out[USB_MAX_ENDPOINTS];
const USBDesc *usb_desc; /* Overrides class usb_desc if not NULL */
const USBDescDevice *device;
int configuration;
int ninterfaces;
int altsetting[USB_MAX_INTERFACES];
const USBDescConfig *config;
const USBDescIface *ifaces[USB_MAX_INTERFACES];
};
typedef struct USBPortOps {
void (*attach)(USBPort *port);
void (*detach)(USBPort *port);
/*
* This gets called when a device downstream from the device attached to
* the port (iow attached through a hub) gets detached.
*/
//void (*child_detach)(USBPort *port, USBDevice *child);
void (*wakeup)(USBPort *port);
/*
* Note that port->dev will be different then the device from which
* the packet originated when a hub is involved.
*/
void (*complete)(USBPort *port, USBPacket *p);
} USBPortOps;
/* USB port on which a device can be connected */
struct USBPort {
USBDevice *dev;
int speedmask;
USBPortOps *ops;
void *opaque;
int index; /* internal port index, may be used with the opaque */
//QTAILQ_ENTRY(USBPort) next; /* Used internally by qemu. */
};
typedef void USBCallback(USBPacket * packet, void *opaque);
typedef enum USBPacketState {
USB_PACKET_UNDEFINED = 0,
USB_PACKET_SETUP,
USB_PACKET_QUEUED,
USB_PACKET_ASYNC,
USB_PACKET_COMPLETE,
USB_PACKET_CANCELED,
} USBPacketState;
/* Structure used to hold information about an active USB packet. */
struct USBPacket {
/* Data fields for use by the driver. */
int pid;
uint64_t id;
USBEndpoint *ep;
unsigned int stream;
QEMUIOVector iov;
uint64_t parameter; /* control transfers */
bool short_not_ok;
bool int_req;
int status; /* USB_RET_* status code */
int actual_length; /* Number of bytes actually transferred */
/* Internal use by the USB layer. */
USBPacketState state;
USBCombinedPacket *combined;
QTAILQ_ENTRY(USBPacket) queue;
QTAILQ_ENTRY(USBPacket) combined_entry;
};
struct USBCombinedPacket {
USBPacket *first;
QTAILQ_HEAD(packets_head, USBPacket) packets;
QEMUIOVector iov;
};
void usb_packet_init(USBPacket *p);
void usb_packet_set_state(USBPacket *p, USBPacketState state);
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
void usb_packet_setup(USBPacket *p, int pid,
USBEndpoint *ep, unsigned int stream,
uint64_t id, bool short_not_ok, bool int_req);
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
//int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
//void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
void usb_packet_skip(USBPacket *p, size_t bytes);
size_t usb_packet_size(USBPacket *p);
void usb_packet_cleanup(USBPacket *p);
static inline bool usb_packet_is_inflight(USBPacket *p)
{
return (p->state == USB_PACKET_QUEUED ||
p->state == USB_PACKET_ASYNC);
}
struct USBBus {
//BusState qbus;
USBBusOps *ops;
int busnr;
int nfree;
int nused;
QTAILQ_HEAD(, USBPort) free;
QTAILQ_HEAD(, USBPort) used;
QTAILQ_ENTRY(USBBus) next;
};
struct USBBusOps {
void (*register_companion)(USBBus *bus, USBPort *ports[],
uint32_t portcount, uint32_t firstport/*,
Error **errp*/);
void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep, unsigned int stream);
};
USBDevice *usb_find_device(USBPort *port, uint8_t addr);
void usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_packet_complete_one(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p);
void usb_ep_init(USBDevice *dev);
void usb_ep_reset(USBDevice *dev);
void usb_ep_dump(USBDevice *dev);
struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep);
uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type);
void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum);
void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw);
void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw);
void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted);
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id);
void usb_ep_combine_input_packets(USBEndpoint *ep);
void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p);
void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p);
void usb_pick_speed(USBPort *port);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
void usb_port_reset(USBPort *port);
void usb_device_reset(USBDevice *dev);
void usb_wakeup(USBEndpoint *ep, unsigned int stream);
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
int usb_generic_handle_packet(USBDevice *s, int pid,
uint8_t devaddr, uint8_t devep,
uint8_t *data, int len);
void usb_reattach(USBPort *port);
void usb_send_msg(USBDevice *dev, int msg);
/* usb hub */
USBDevice *usb_hub_init(int nb_ports);
USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr);
void usb_device_cancel_packet(USBDevice *dev, USBPacket *p);
void usb_device_handle_attach(USBDevice *dev);
void usb_device_handle_reset(USBDevice *dev);
void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
int val, int index, int length, uint8_t *data);
void usb_device_handle_data(USBDevice *dev, USBPacket *p);
void usb_device_set_interface(USBDevice *dev, int intf,
int alt_old, int alt_new);
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep);
void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep);
int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps,
int streams);
void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps);
const char *usb_device_get_product_desc(USBDevice *dev);
const USBDesc *usb_device_get_usb_desc(USBDevice *dev);

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

25
pcsx2/USB/qemu-usb/vl.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "vl.h"
/* 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;
}

86
pcsx2/USB/qemu-usb/vl.h Normal file
View File

@ -0,0 +1,86 @@
/*
* 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 <cstdlib>
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <cstdint>
#include <cstddef>
#include <limits.h>
#include <time.h>
#include <ctype.h>
#include <cassert>
#include <cerrno>
#include <fcntl.h>
#include <sys/stat.h>
#if !defined(_MSC_VER)
#define inline __inline
#endif
#ifdef _WIN32
#define PRId64 "I64d"
#define PRIx64 "I64x"
#define PRIu64 "I64u"
#define PRIo64 "I64o"
#endif
#include "qusb.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);
int cpu_physical_memory_rw(uint32_t addr, uint8_t *buf,
size_t len, int is_write);
inline int cpu_physical_memory_read(uint32_t addr,
uint8_t *buf, size_t len)
{
return cpu_physical_memory_rw(addr, buf, len, 0);
}
inline int cpu_physical_memory_write(uint32_t addr,
const uint8_t *buf, size_t len)
{
return cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
}
#endif /* VL_H */

View File

@ -0,0 +1,28 @@
This license applies to all the code in this repository except that written by third
parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff
Preshing's semaphore implementation (used in the blocking queue) which has a zlib
license (embedded in atomicops.h).
Simplified BSD License:
Copyright (c) 2013-2015, Cameron Desrochers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,122 @@
# A single-producer, single-consumer lock-free queue for C++
This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though
you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!).
Note: If you need a general-purpose multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc].
## Features
- [Blazing fast][benchmarks]
- Compatible with C++11 (supports moving objects instead of making copies)
- Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself
(which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing)
- Allocates memory up front, in contiguous blocks
- Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity)
- Also provides an `enqueue` method which can dynamically grow the size of the queue as needed
- Also provides `try_emplace`/`emplace` convenience methods
- Has a blocking version with `wait_dequeue`
- Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation)
- On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches)
## Use
Simply drop the readerwriterqueue.h and atomicops.h files into your source code and include them :-)
A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work).
Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives
from working correctly.
Example:
```cpp
using namespace moodycamel;
ReaderWriterQueue<int> q(100); // Reserve space for at least 100 elements up front
q.enqueue(17); // Will allocate memory if the queue is full
bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates)
assert(succeeded);
int number;
succeeded = q.try_dequeue(number); // Returns false if the queue was empty
assert(succeeded && number == 17);
// You can also peek at the front item of the queue (consumer only)
int* front = q.peek();
assert(*front == 18);
succeeded = q.try_dequeue(number);
assert(succeeded && number == 18);
front = q.peek();
assert(front == nullptr); // Returns nullptr if the queue was empty
```
The blocking version has the exact same API, with the addition of `wait_dequeue` and
`wait_dequeue_timed` methods:
```cpp
BlockingReaderWriterQueue<int> q;
std::thread reader([&]() {
int item;
for (int i = 0; i != 100; ++i) {
// Fully-blocking:
q.wait_dequeue(item);
// Blocking with timeout
if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5)))
++i;
}
});
std::thread writer([&]() {
for (int i = 0; i != 100; ++i) {
q.enqueue(i);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
writer.join();
reader.join();
assert(q.size_approx() == 0);
```
Note that `wait_dequeue` will block indefinitely while the queue is empty; this
means care must be taken to only call `wait_dequeue` if you're sure another element
will come along eventually, or if the queue has a static lifetime. This is because
destroying the queue while a thread is waiting on it will invoke undefined behaviour.
## Disclaimers
The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that
includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-)
Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on
anything that's not x86-based.
Finally, I am not an expert. This is my first foray into lock-free programming, and though I'm confident in the code,
it's possible that there are bugs despite the effort I put into designing and testing this data structure.
Use this code at your own risk; in particular, lock-free programming is a patent minefield, and this code may very
well violate a pending patent (I haven't looked). It's worth noting that I came up with this algorithm and
implementation from scratch, independent of any existing lock-free queues.
## More info
See the [LICENSE.md][license] file for the license (simplified BSD).
My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious
about lock-free programming.
[blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
[license]: LICENSE.md
[benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks
[gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference
[mpmc]: https://github.com/cameron314/concurrentqueue

View File

@ -0,0 +1,665 @@
// ©2013-2016 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
// Uses Jeff Preshing's semaphore implementation (under the terms of its
// separate zlib license, embedded below).
#pragma once
// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation
// of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment).
// Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees).
// Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols.
#include <cassert>
#include <type_traits>
#include <cerrno>
#include <cstdint>
#include <ctime>
// Platform detection
#if defined(__INTEL_COMPILER)
#define AE_ICC
#elif defined(_MSC_VER)
#define AE_VCPP
#elif defined(__GNUC__)
#define AE_GCC
#endif
#if defined(_M_IA64) || defined(__ia64__)
#define AE_ARCH_IA64
#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
#define AE_ARCH_X64
#elif defined(_M_IX86) || defined(__i386__)
#define AE_ARCH_X86
#elif defined(_M_PPC) || defined(__powerpc__)
#define AE_ARCH_PPC
#else
#define AE_ARCH_UNKNOWN
#endif
// AE_UNUSED
#define AE_UNUSED(x) ((void)x)
// AE_FORCEINLINE
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_FORCEINLINE __forceinline
#elif defined(AE_GCC)
//#define AE_FORCEINLINE __attribute__((always_inline))
#define AE_FORCEINLINE inline
#else
#define AE_FORCEINLINE inline
#endif
// AE_ALIGN
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_ALIGN(x) __declspec(align(x))
#elif defined(AE_GCC)
#define AE_ALIGN(x) __attribute__((aligned(x)))
#else
// Assume GCC compliant syntax...
#define AE_ALIGN(x) __attribute__((aligned(x)))
#endif
// Portable atomic fences implemented below:
namespace moodycamel {
enum memory_order {
memory_order_relaxed,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
// memory_order_sync: Forces a full sync:
// #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
memory_order_sync = memory_order_seq_cst
};
} // end namespace moodycamel
#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || (defined(AE_ICC) && __INTEL_COMPILER < 1600)
// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
#include <intrin.h>
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
#define AeFullSync _mm_mfence
#define AeLiteSync _mm_mfence
#elif defined(AE_ARCH_IA64)
#define AeFullSync __mf
#define AeLiteSync __mf
#elif defined(AE_ARCH_PPC)
#include <ppcintrinsics.h>
#define AeFullSync __sync
#define AeLiteSync __lwsync
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert`
#ifdef __cplusplus_cli
#pragma managed(push, off)
#endif
#endif
namespace moodycamel {
AE_FORCEINLINE void compiler_fence(memory_order order)
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: _ReadBarrier(); break;
case memory_order_release: _WriteBarrier(); break;
case memory_order_acq_rel: _ReadWriteBarrier(); break;
case memory_order_seq_cst: _ReadWriteBarrier(); break;
default: assert(false);
}
}
// x86/x64 have a strong memory model -- all loads and stores have
// acquire and release semantics automatically (so only need compiler
// barriers for those).
#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
AE_FORCEINLINE void fence(memory_order order)
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: _ReadBarrier(); break;
case memory_order_release: _WriteBarrier(); break;
case memory_order_acq_rel: _ReadWriteBarrier(); break;
case memory_order_seq_cst:
_ReadWriteBarrier();
AeFullSync();
_ReadWriteBarrier();
break;
default: assert(false);
}
}
#else
AE_FORCEINLINE void fence(memory_order order)
{
// Non-specialized arch, use heavier memory barriers everywhere just in case :-(
switch (order) {
case memory_order_relaxed:
break;
case memory_order_acquire:
_ReadBarrier();
AeLiteSync();
_ReadBarrier();
break;
case memory_order_release:
_WriteBarrier();
AeLiteSync();
_WriteBarrier();
break;
case memory_order_acq_rel:
_ReadWriteBarrier();
AeLiteSync();
_ReadWriteBarrier();
break;
case memory_order_seq_cst:
_ReadWriteBarrier();
AeFullSync();
_ReadWriteBarrier();
break;
default: assert(false);
}
}
#endif
} // end namespace moodycamel
#else
// Use standard library of atomics
#include <atomic>
namespace moodycamel {
AE_FORCEINLINE void compiler_fence(memory_order order)
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break;
case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break;
case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break;
case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break;
default: assert(false);
}
}
AE_FORCEINLINE void fence(memory_order order)
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: std::atomic_thread_fence(std::memory_order_acquire); break;
case memory_order_release: std::atomic_thread_fence(std::memory_order_release); break;
case memory_order_acq_rel: std::atomic_thread_fence(std::memory_order_acq_rel); break;
case memory_order_seq_cst: std::atomic_thread_fence(std::memory_order_seq_cst); break;
default: assert(false);
}
}
} // end namespace moodycamel
#endif
#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli))
#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
#endif
#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
#include <atomic>
#endif
#include <utility>
// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
// Provides basic support for atomic variables -- no memory ordering guarantees are provided.
// The guarantee of atomicity is only made for types that already have atomic load and store guarantees
// at the hardware level -- on most platforms this generally means aligned pointers and integers (only).
namespace moodycamel {
template<typename T>
class weak_atomic
{
public:
weak_atomic() { }
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning
#endif
template<typename U> weak_atomic(U&& x) : value(std::forward<U>(x)) { }
#ifdef __cplusplus_cli
// Work around bug with universal reference/nullptr combination that only appears when /clr is on
weak_atomic(nullptr_t) : value(nullptr) { }
#endif
weak_atomic(weak_atomic const& other) : value(other.value) { }
weak_atomic(weak_atomic&& other) : value(std::move(other.value)) { }
#ifdef AE_VCPP
#pragma warning(pop)
#endif
AE_FORCEINLINE operator T() const { return load(); }
#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
template<typename U> AE_FORCEINLINE weak_atomic const& operator=(U&& x) { value = std::forward<U>(x); return *this; }
AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) { value = other.value; return *this; }
AE_FORCEINLINE T load() const { return value; }
AE_FORCEINLINE T fetch_add_acquire(T increment)
{
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
#if defined(_M_AMD64)
else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
#endif
#else
#error Unsupported platform
#endif
assert(false && "T must be either a 32 or 64 bit type");
return value;
}
AE_FORCEINLINE T fetch_add_release(T increment)
{
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
#if defined(_M_AMD64)
else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
#endif
#else
#error Unsupported platform
#endif
assert(false && "T must be either a 32 or 64 bit type");
return value;
}
#else
template<typename U>
AE_FORCEINLINE weak_atomic const& operator=(U&& x)
{
value.store(std::forward<U>(x), std::memory_order_relaxed);
return *this;
}
AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other)
{
value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
return *this;
}
AE_FORCEINLINE T load() const { return value.load(std::memory_order_relaxed); }
AE_FORCEINLINE T fetch_add_acquire(T increment)
{
return value.fetch_add(increment, std::memory_order_acquire);
}
AE_FORCEINLINE T fetch_add_release(T increment)
{
return value.fetch_add(increment, std::memory_order_release);
}
#endif
private:
#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
// No std::atomic support, but still need to circumvent compiler optimizations.
// `volatile` will make memory access slow, but is guaranteed to be reliable.
volatile T value;
#else
std::atomic<T> value;
#endif
};
} // end namespace moodycamel
// Portable single-producer, single-consumer semaphore below:
#if defined(_WIN32)
// Avoid including windows.h in a header; we only need a handful of
// items, so we'll redeclare them here (this is relatively safe since
// the API generally has to remain stable between Windows versions).
// I know this is an ugly hack but it still beats polluting the global
// namespace with thousands of generic names or adding a .cpp for nothing.
extern "C" {
struct _SECURITY_ATTRIBUTES;
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
}
#elif defined(__MACH__)
#include <mach/mach.h>
#elif defined(__unix__)
#include <semaphore.h>
#endif
namespace moodycamel
{
// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's
// portable + lightweight semaphore implementations, originally from
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
// LICENSE:
// Copyright (c) 2015 Jeff Preshing
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
namespace spsc_sema
{
#if defined(_WIN32)
class Semaphore
{
private:
void* m_hSema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
const long maxLong = 0x7fffffff;
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
}
~Semaphore()
{
CloseHandle(m_hSema);
}
void wait()
{
const unsigned long infinite = 0xffffffff;
WaitForSingleObject(m_hSema, infinite);
}
bool try_wait()
{
const unsigned long RC_WAIT_TIMEOUT = 0x00000102;
return WaitForSingleObject(m_hSema, 0) != RC_WAIT_TIMEOUT;
}
bool timed_wait(std::uint64_t usecs)
{
const unsigned long RC_WAIT_TIMEOUT = 0x00000102;
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) != RC_WAIT_TIMEOUT;
}
void signal(int count = 1)
{
ReleaseSemaphore(m_hSema, count, nullptr);
}
};
#elif defined(__MACH__)
//---------------------------------------------------------
// Semaphore (Apple iOS and OSX)
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
//---------------------------------------------------------
class Semaphore
{
private:
semaphore_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
}
~Semaphore()
{
semaphore_destroy(mach_task_self(), m_sema);
}
void wait()
{
semaphore_wait(m_sema);
}
bool try_wait()
{
return timed_wait(0);
}
bool timed_wait(std::int64_t timeout_usecs)
{
mach_timespec_t ts;
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
ts.tv_nsec = (timeout_usecs % 1000000) * 1000;
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
kern_return_t rc = semaphore_timedwait(m_sema, ts);
return rc != KERN_OPERATION_TIMED_OUT && rc != KERN_ABORTED;
}
void signal()
{
semaphore_signal(m_sema);
}
void signal(int count)
{
while (count-- > 0)
{
semaphore_signal(m_sema);
}
}
};
#elif defined(__unix__)
//---------------------------------------------------------
// Semaphore (POSIX, Linux)
//---------------------------------------------------------
class Semaphore
{
private:
sem_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
sem_init(&m_sema, 0, initialCount);
}
~Semaphore()
{
sem_destroy(&m_sema);
}
void wait()
{
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
int rc;
do
{
rc = sem_wait(&m_sema);
}
while (rc == -1 && errno == EINTR);
}
bool try_wait()
{
int rc;
do {
rc = sem_trywait(&m_sema);
} while (rc == -1 && errno == EINTR);
return !(rc == -1 && errno == EAGAIN);
}
bool timed_wait(std::uint64_t usecs)
{
struct timespec ts;
const int usecs_in_1_sec = 1000000;
const int nsecs_in_1_sec = 1000000000;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += usecs / usecs_in_1_sec;
ts.tv_nsec += (usecs % usecs_in_1_sec) * 1000;
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
// so we have to clean things up before passing it in
if (ts.tv_nsec >= nsecs_in_1_sec) {
ts.tv_nsec -= nsecs_in_1_sec;
++ts.tv_sec;
}
int rc;
do {
rc = sem_timedwait(&m_sema, &ts);
} while (rc == -1 && errno == EINTR);
return !(rc == -1 && errno == ETIMEDOUT);
}
void signal()
{
sem_post(&m_sema);
}
void signal(int count)
{
while (count-- > 0)
{
sem_post(&m_sema);
}
}
};
#else
#error Unsupported platform! (No semaphore wrapper available)
#endif
//---------------------------------------------------------
// LightweightSemaphore
//---------------------------------------------------------
class LightweightSemaphore
{
public:
typedef std::make_signed<std::size_t>::type ssize_t;
private:
weak_atomic<ssize_t> m_count;
Semaphore m_sema;
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
{
ssize_t oldCount;
// Is there a better way to set the initial spin count?
// If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
// as threads start hitting the kernel semaphore.
int spin = 10000;
while (--spin >= 0)
{
if (m_count.load() > 0)
{
m_count.fetch_add_acquire(-1);
return true;
}
compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop.
}
oldCount = m_count.fetch_add_acquire(-1);
if (oldCount > 0)
return true;
if (timeout_usecs < 0)
{
m_sema.wait();
return true;
}
if (m_sema.timed_wait(timeout_usecs))
return true;
// At this point, we've timed out waiting for the semaphore, but the
// count is still decremented indicating we may still be waiting on
// it. So we have to re-adjust the count, but only if the semaphore
// wasn't signaled enough times for us too since then. If it was, we
// need to release the semaphore too.
while (true)
{
oldCount = m_count.fetch_add_release(1);
if (oldCount < 0)
return false; // successfully restored things to the way they were
// Oh, the producer thread just signaled the semaphore after all. Try again:
oldCount = m_count.fetch_add_acquire(-1);
if (oldCount > 0 && m_sema.try_wait())
return true;
}
}
public:
LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount)
{
assert(initialCount >= 0);
}
bool tryWait()
{
if (m_count.load() > 0)
{
m_count.fetch_add_acquire(-1);
return true;
}
return false;
}
void wait()
{
if (!tryWait())
waitWithPartialSpinning();
}
bool wait(std::int64_t timeout_usecs)
{
return tryWait() || waitWithPartialSpinning(timeout_usecs);
}
void signal(ssize_t count = 1)
{
assert(count >= 0);
ssize_t oldCount = m_count.fetch_add_release(count);
assert(oldCount >= -1);
if (oldCount < 0)
{
m_sema.signal(1);
}
}
ssize_t availableApprox() const
{
ssize_t count = m_count.load();
return count > 0 ? count : 0;
}
};
} // end namespace spsc_sema
} // end namespace moodycamel
#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))
#pragma warning(pop)
#ifdef __cplusplus_cli
#pragma managed(pop)
#endif
#endif

View File

@ -0,0 +1,894 @@
// ©2013-2016 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
#pragma once
#include "atomicops.h"
#include <type_traits>
#include <utility>
#include <cassert>
#include <stdexcept>
#include <new>
#include <cstdint>
#include <cstdlib> // For malloc/free/abort & size_t
#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
#include <chrono>
#endif
// A lock-free queue for a single-consumer, single-producer architecture.
// The queue is also wait-free in the common path (except if more memory
// needs to be allocated, in which case malloc is called).
// Allocates memory sparingly (O(lg(n) times, amortized), and only once if
// the original maximum size estimate is never exceeded.
// Tested on x86/x64 processors, but semantics should be correct for all
// architectures (given the right implementations in atomicops.h), provided
// that aligned integer and pointer accesses are naturally atomic.
// Note that there should only be one consumer thread and producer thread;
// Switching roles of the threads, or using multiple consecutive threads for
// one role, is not safe unless properly synchronized.
// Using the queue exclusively from one thread is fine, though a bit silly.
#ifndef MOODYCAMEL_CACHE_LINE_SIZE
#define MOODYCAMEL_CACHE_LINE_SIZE 64
#endif
#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
#define MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#endif
#ifndef MOODYCAMEL_HAS_EMPLACE
#if !defined(_MSC_VER) || _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
#define MOODYCAMEL_HAS_EMPLACE 1
#endif
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4324) // structure was padded due to __declspec(align())
#pragma warning(disable: 4820) // padding was added
#pragma warning(disable: 4127) // conditional expression is constant
#endif
namespace moodycamel {
template<typename T, size_t MAX_BLOCK_SIZE = 512>
class ReaderWriterQueue
{
// Design: Based on a queue-of-queues. The low-level queues are just
// circular buffers with front and tail indices indicating where the
// next element to dequeue is and where the next element can be enqueued,
// respectively. Each low-level queue is called a "block". Each block
// wastes exactly one element's worth of space to keep the design simple
// (if front == tail then the queue is empty, and can't be full).
// The high-level queue is a circular linked list of blocks; again there
// is a front and tail, but this time they are pointers to the blocks.
// The front block is where the next element to be dequeued is, provided
// the block is not empty. The back block is where elements are to be
// enqueued, provided the block is not full.
// The producer thread owns all the tail indices/pointers. The consumer
// thread owns all the front indices/pointers. Both threads read each
// other's variables, but only the owning thread updates them. E.g. After
// the consumer reads the producer's tail, the tail may change before the
// consumer is done dequeuing an object, but the consumer knows the tail
// will never go backwards, only forwards.
// If there is no room to enqueue an object, an additional block (of
// equal size to the last block) is added. Blocks are never removed.
public:
typedef T value_type;
// Constructs a queue that can hold maxSize elements without further
// allocations. If more than MAX_BLOCK_SIZE elements are requested,
// then several blocks of MAX_BLOCK_SIZE each are reserved (including
// at least one extra buffer block).
explicit ReaderWriterQueue(size_t maxSize = 15)
#ifndef NDEBUG
: enqueuing(false)
,dequeuing(false)
#endif
{
assert(maxSize > 0);
assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2");
assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
Block* firstBlock = nullptr;
largestBlockSize = ceilToPow2(maxSize + 1); // We need a spare slot to fit maxSize elements in the block
if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
// We need a spare block in case the producer is writing to a different block the consumer is reading from, and
// wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity
// between front == tail meaning "empty" and "full".
// So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the
// number of blocks - 1. Solving for maxSize and applying a ceiling to the division gives us (after simplifying):
size_t initialBlockCount = (maxSize + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
largestBlockSize = MAX_BLOCK_SIZE;
Block* lastBlock = nullptr;
for (size_t i = 0; i != initialBlockCount; ++i) {
auto block = make_block(largestBlockSize);
if (block == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
if (firstBlock == nullptr) {
firstBlock = block;
}
else {
lastBlock->next = block;
}
lastBlock = block;
block->next = firstBlock;
}
}
else {
firstBlock = make_block(largestBlockSize);
if (firstBlock == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
firstBlock->next = firstBlock;
}
frontBlock = firstBlock;
tailBlock = firstBlock;
// Make sure the reader/writer threads will have the initialized memory setup above:
fence(memory_order_sync);
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
ReaderWriterQueue(ReaderWriterQueue&& other)
: frontBlock(other.frontBlock.load()),
tailBlock(other.tailBlock.load()),
largestBlockSize(other.largestBlockSize)
#ifndef NDEBUG
,enqueuing(false)
,dequeuing(false)
#endif
{
other.largestBlockSize = 32;
Block* b = other.make_block(other.largestBlockSize);
if (b == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
b->next = b;
other.frontBlock = b;
other.tailBlock = b;
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
ReaderWriterQueue& operator=(ReaderWriterQueue&& other)
{
Block* b = frontBlock.load();
frontBlock = other.frontBlock.load();
other.frontBlock = b;
b = tailBlock.load();
tailBlock = other.tailBlock.load();
other.tailBlock = b;
std::swap(largestBlockSize, other.largestBlockSize);
return *this;
}
// Note: The queue should not be accessed concurrently while it's
// being deleted. It's up to the user to synchronize this.
~ReaderWriterQueue()
{
// Make sure we get the latest version of all variables from other CPUs:
fence(memory_order_sync);
// Destroy any remaining objects in queue and free memory
Block* frontBlock_ = frontBlock;
Block* block = frontBlock_;
do {
Block* nextBlock = block->next;
size_t blockFront = block->front;
size_t blockTail = block->tail;
for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
element->~T();
(void)element;
}
auto rawBlock = block->rawThis;
block->~Block();
std::free(rawBlock);
block = nextBlock;
} while (block != frontBlock_);
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element)
{
return inner_enqueue<CannotAlloc>(element);
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element)
{
return inner_enqueue<CannotAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args)
{
return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element)
{
return inner_enqueue<CanAlloc>(element);
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element)
{
return inner_enqueue<CanAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args)
{
return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template<typename U>
bool try_dequeue(U& result)
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// High-level pseudocode:
// Remember where the tail block is
// If the front block has an element in it, dequeue it
// Else
// If front block was the tail block when we entered the function, return false
// Else advance to next block and dequeue the item there
// Note that we have to use the value of the tail block from before we check if the front
// block is full or not, in case the front block is empty and then, before we check if the
// tail block is at the front block or not, the producer fills up the front block *and
// moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
// reproducible in practice.
// In order to avoid overhead in the common case, though, we do a double-checked pattern
// where we have the fast path if the front block is not empty, then read the tail block,
// then re-read the front block and check if it's not empty again, then check if the tail
// block has advanced.
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
// Front block not empty, dequeue from here
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
result = std::move(*element);
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
// Oh look, the front block isn't empty after all
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
// Don't need an acquire fence here since next can only ever be set on the tailBlock,
// and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which
// ensures next is up-to-date on this CPU in case we recently were at tailBlock.
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
// Since the tailBlock is only ever advanced after being written to,
// we know there's for sure an element to dequeue on it
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
// We're done with this block, let the producer use it if it needs
fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release); // Not strictly needed
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
result = std::move(*element);
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
}
else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
T* peek()
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlock->tail.load());
return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
}
return nullptr;
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
bool pop()
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
fence(memory_order_release);
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release);
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
}
else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
inline size_t size_approx() const
{
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
size_t blockFront = block->front.load();
size_t blockTail = block->tail.load();
result += (blockTail - blockFront) & block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
private:
enum AllocationMode { CanAlloc, CannotAlloc };
#if MOODYCAMEL_HAS_EMPLACE
template<AllocationMode canAlloc, typename... Args>
bool inner_enqueue(Args&&... args)
#else
template<AllocationMode canAlloc, typename U>
bool inner_enqueue(U&& element)
#endif
{
#ifndef NDEBUG
ReentrantGuard guard(this->enqueuing);
#endif
// High-level pseudocode (assuming we're allowed to alloc a new block):
// If room in tail block, add to tail
// Else check next block
// If next block is not the head block, enqueue on next block
// Else create a new block and enqueue there
// Advance tail to the block we just enqueued to
Block* tailBlock_ = tailBlock.load();
size_t blockFront = tailBlock_->localFront;
size_t blockTail = tailBlock_->tail.load();
size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
fence(memory_order_acquire);
// This block has room for at least one more element
char* location = tailBlock_->data + blockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
fence(memory_order_release);
tailBlock_->tail = nextBlockTail;
}
else {
fence(memory_order_acquire);
if (tailBlock_->next.load() != frontBlock) {
// Note that the reason we can't advance to the frontBlock and start adding new entries there
// is because if we did, then dequeue would stay in that block, eventually reading the new values,
// instead of advancing to the next full block (whose values were enqueued first and so should be
// consumed first).
fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock
// tailBlock is full, but there's a free block ahead, use it
Block* tailBlockNext = tailBlock_->next.load();
size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
nextBlockTail = tailBlockNext->tail.load();
fence(memory_order_acquire);
// This block must be empty since it's not the head block and we
// go through the blocks in a circle
assert(nextBlockFront == nextBlockTail);
tailBlockNext->localFront = nextBlockFront;
char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
fence(memory_order_release);
tailBlock = tailBlockNext;
}
else if (canAlloc == CanAlloc) {
// tailBlock is full and there's no free block ahead; create a new block
auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
auto newBlock = make_block(newBlockSize);
if (newBlock == nullptr) {
// Could not allocate a block!
return false;
}
largestBlockSize = newBlockSize;
#if MOODYCAMEL_HAS_EMPLACE
new (newBlock->data) T(std::forward<Args>(args)...);
#else
new (newBlock->data) T(std::forward<U>(element));
#endif
assert(newBlock->front == 0);
newBlock->tail = newBlock->localTail = 1;
newBlock->next = tailBlock_->next.load();
tailBlock_->next = newBlock;
// Might be possible for the dequeue thread to see the new tailBlock->next
// *without* seeing the new tailBlock value, but this is OK since it can't
// advance to the next block until tailBlock is set anyway (because the only
// case where it could try to read the next is if it's already at the tailBlock,
// and it won't advance past tailBlock in any circumstance).
fence(memory_order_release);
tailBlock = newBlock;
}
else if (canAlloc == CannotAlloc) {
// Would have had to allocate a new block to enqueue, but not allowed
return false;
}
else {
assert(false && "Should be unreachable code");
return false;
}
}
return true;
}
// Disable copying
ReaderWriterQueue(ReaderWriterQueue const&) { }
// Disable assignment
ReaderWriterQueue& operator=(ReaderWriterQueue const&) { }
AE_FORCEINLINE static size_t ceilToPow2(size_t x)
{
// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
x |= x >> (i << 3);
}
++x;
return x;
}
template<typename U>
static AE_FORCEINLINE char* align_for(char* ptr)
{
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
private:
#ifndef NDEBUG
struct ReentrantGuard
{
ReentrantGuard(bool& _inSection)
: inSection(_inSection)
{
assert(!inSection && "ReaderWriterQueue does not support enqueuing or dequeuing elements from other elements' ctors and dtors");
inSection = true;
}
~ReentrantGuard() { inSection = false; }
private:
ReentrantGuard& operator=(ReentrantGuard const&);
private:
bool& inSection;
};
#endif
struct Block
{
// Avoid false-sharing by putting highly contended variables on their own cache lines
weak_atomic<size_t> front; // (Atomic) Elements are read from here
size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)];
weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
size_t localFront;
char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is)
weak_atomic<Block*> next; // (Atomic)
char* data; // Contents (on heap) are aligned to T's alignment
const size_t sizeMask;
// size must be a power of two (and greater than 0)
Block(size_t const& _size, char* _rawThis, char* _data)
: front(0), localTail(0), tail(0), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis)
{
}
private:
// C4512 - Assignment operator could not be generated
Block& operator=(Block const&);
public:
char* rawThis;
};
static Block* make_block(size_t capacity)
{
// Allocate enough memory for the block itself, as well as all the elements it will contain
auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
auto newBlockRaw = static_cast<char*>(std::malloc(size));
if (newBlockRaw == nullptr) {
return nullptr;
}
auto newBlockAligned = align_for<Block>(newBlockRaw);
auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
}
private:
weak_atomic<Block*> frontBlock; // (Atomic) Elements are enqueued to this block
char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
weak_atomic<Block*> tailBlock; // (Atomic) Elements are dequeued from this block
size_t largestBlockSize;
#ifndef NDEBUG
bool enqueuing;
bool dequeuing;
#endif
};
// Like ReaderWriterQueue, but also providees blocking operations
template<typename T, size_t MAX_BLOCK_SIZE = 512>
class BlockingReaderWriterQueue
{
private:
typedef ::moodycamel::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
public:
explicit BlockingReaderWriterQueue(size_t maxSize = 15)
: inner(maxSize)
{ }
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element)
{
if (inner.try_enqueue(element)) {
sema.signal();
return true;
}
return false;
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element)
{
if (inner.try_enqueue(std::forward<T>(element))) {
sema.signal();
return true;
}
return false;
}
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element)
{
if (inner.enqueue(element)) {
sema.signal();
return true;
}
return false;
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element)
{
if (inner.enqueue(std::forward<T>(element))) {
sema.signal();
return true;
}
return false;
}
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template<typename U>
bool try_dequeue(U& result)
{
if (sema.tryWait()) {
bool success = inner.try_dequeue(result);
assert(success);
AE_UNUSED(success);
return true;
}
return false;
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available, then dequeues it.
template<typename U>
void wait_dequeue(U& result)
{
sema.wait();
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template<typename U>
bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs)
{
if (!sema.wait(timeout_usecs)) {
return false;
}
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
return true;
}
#if __cplusplus > 199711L || _MSC_VER >= 1700
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& result, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
#endif
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
AE_FORCEINLINE T* peek()
{
return inner.peek();
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
AE_FORCEINLINE bool pop()
{
if (sema.tryWait()) {
bool result = inner.pop();
assert(result);
AE_UNUSED(result);
return true;
}
return false;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
AE_FORCEINLINE size_t size_approx() const
{
return sema.availableApprox();
}
private:
// Disable copying & assignment
BlockingReaderWriterQueue(ReaderWriterQueue const&) { }
BlockingReaderWriterQueue& operator=(ReaderWriterQueue const&) { }
private:
ReaderWriterQueue inner;
spsc_sema::LightweightSemaphore sema;
};
} // end namespace moodycamel
#ifdef AE_VCPP
#pragma warning(pop)
#endif

View File

@ -0,0 +1,53 @@
#include <windows.h>
#include <setupapi.h>
#include "hidapi.h"
_HidD_GetHidGuid HidD_GetHidGuid = NULL;
_HidD_GetAttributes HidD_GetAttributes = NULL;
_HidD_GetPreparsedData HidD_GetPreparsedData = NULL;
_HidP_GetCaps HidP_GetCaps = NULL;
_HidD_FreePreparsedData HidD_FreePreparsedData = NULL;
_HidD_GetFeature HidD_GetFeature = NULL;
_HidD_SetFeature HidD_SetFeature = NULL;
_HidP_GetSpecificButtonCaps HidP_GetSpecificButtonCaps = NULL;
_HidP_GetButtonCaps HidP_GetButtonCaps = NULL;
_HidP_GetUsages HidP_GetUsages = NULL;
_HidP_GetValueCaps HidP_GetValueCaps = NULL;
_HidP_GetUsageValue HidP_GetUsageValue = NULL;
_HidD_GetProductString HidD_GetProductString = NULL;
static HMODULE hModHid = 0;
int InitHid() {
if (hModHid) {
return 1;
}
hModHid = LoadLibraryA("hid.dll");
if (hModHid) {
if ((HidD_GetHidGuid = (_HidD_GetHidGuid)GetProcAddress(hModHid, "HidD_GetHidGuid")) &&
(HidD_GetAttributes = (_HidD_GetAttributes)GetProcAddress(hModHid, "HidD_GetAttributes")) &&
(HidD_GetPreparsedData = (_HidD_GetPreparsedData)GetProcAddress(hModHid, "HidD_GetPreparsedData")) &&
(HidP_GetCaps = (_HidP_GetCaps)GetProcAddress(hModHid, "HidP_GetCaps")) &&
(HidD_FreePreparsedData = (_HidD_FreePreparsedData)GetProcAddress(hModHid, "HidD_FreePreparsedData")) &&
(HidP_GetSpecificButtonCaps = (_HidP_GetSpecificButtonCaps)GetProcAddress(hModHid, "HidP_GetSpecificButtonCaps")) &&
(HidP_GetButtonCaps = (_HidP_GetButtonCaps)GetProcAddress(hModHid, "HidP_GetButtonCaps")) &&
(HidP_GetUsages = (_HidP_GetUsages)GetProcAddress(hModHid, "HidP_GetUsages")) &&
(HidP_GetValueCaps = (_HidP_GetValueCaps)GetProcAddress(hModHid, "HidP_GetValueCaps")) &&
(HidP_GetUsageValue = (_HidP_GetUsageValue)GetProcAddress(hModHid, "HidP_GetUsageValue")) &&
(HidD_GetProductString = (_HidD_GetProductString)GetProcAddress(hModHid, "HidD_GetProductString")) &&
(HidD_GetFeature = (_HidD_GetFeature)GetProcAddress(hModHid, "HidD_GetFeature")) &&
(HidD_SetFeature = (_HidD_SetFeature)GetProcAddress(hModHid, "HidD_SetFeature"))) {
//pHidD_GetHidGuid(&GUID_DEVINTERFACE_HID);
return 1;
}
UninitHid();
}
return 0;
}
void UninitHid() {
if (hModHid) {
FreeLibrary(hModHid);
hModHid = 0;
}
}

451
pcsx2/USB/shared/hidapi.h Normal file
View File

@ -0,0 +1,451 @@
#ifndef HIDAPI_H
#define HIDAPI_H
#include <pshpack4.h>
typedef USHORT USAGE, *PUSAGE;
#define HID_USAGE_PAGE_GENERIC ((USAGE) 0x01)
#define HID_USAGE_PAGE_SIMULATION ((USAGE) 0x02)
#define HID_USAGE_PAGE_VR ((USAGE) 0x03)
#define HID_USAGE_PAGE_SPORT ((USAGE) 0x04)
#define HID_USAGE_PAGE_GAME ((USAGE) 0x05)
#define HID_USAGE_PAGE_KEYBOARD ((USAGE) 0x07)
#define HID_USAGE_PAGE_LED ((USAGE) 0x08)
#define HID_USAGE_PAGE_BUTTON ((USAGE) 0x09)
#define HID_USAGE_PAGE_ORDINAL ((USAGE) 0x0A)
#define HID_USAGE_PAGE_TELEPHONY ((USAGE) 0x0B)
#define HID_USAGE_PAGE_CONSUMER ((USAGE) 0x0C)
#define HID_USAGE_PAGE_DIGITIZER ((USAGE) 0x0D)
#define HID_USAGE_PAGE_UNICODE ((USAGE) 0x10)
#define HID_USAGE_PAGE_ALPHANUMERIC ((USAGE) 0x14)
//
// Usages from Generic Desktop Page (0x01)
//
#define HID_USAGE_GENERIC_POINTER ((USAGE) 0x01)
#define HID_USAGE_GENERIC_MOUSE ((USAGE) 0x02)
#define HID_USAGE_GENERIC_JOYSTICK ((USAGE) 0x04)
#define HID_USAGE_GENERIC_GAMEPAD ((USAGE) 0x05)
#define HID_USAGE_GENERIC_KEYBOARD ((USAGE) 0x06)
#define HID_USAGE_GENERIC_KEYPAD ((USAGE) 0x07)
#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE) 0x80)
#define HID_USAGE_GENERIC_X ((USAGE) 0x30)
#define HID_USAGE_GENERIC_Y ((USAGE) 0x31)
#define HID_USAGE_GENERIC_Z ((USAGE) 0x32)
#define HID_USAGE_GENERIC_RX ((USAGE) 0x33)
#define HID_USAGE_GENERIC_RY ((USAGE) 0x34)
#define HID_USAGE_GENERIC_RZ ((USAGE) 0x35)
#define HID_USAGE_GENERIC_SLIDER ((USAGE) 0x36)
#define HID_USAGE_GENERIC_DIAL ((USAGE) 0x37)
#define HID_USAGE_GENERIC_WHEEL ((USAGE) 0x38)
#define HID_USAGE_GENERIC_HATSWITCH ((USAGE) 0x39)
#define HID_USAGE_GENERIC_COUNTED_BUFFER ((USAGE) 0x3A)
#define HID_USAGE_GENERIC_BYTE_COUNT ((USAGE) 0x3B)
#define HID_USAGE_GENERIC_MOTION_WAKEUP ((USAGE) 0x3C)
#define HID_USAGE_GENERIC_VX ((USAGE) 0x40)
#define HID_USAGE_GENERIC_VY ((USAGE) 0x41)
#define HID_USAGE_GENERIC_VZ ((USAGE) 0x42)
#define HID_USAGE_GENERIC_VBRX ((USAGE) 0x43)
#define HID_USAGE_GENERIC_VBRY ((USAGE) 0x44)
#define HID_USAGE_GENERIC_VBRZ ((USAGE) 0x45)
#define HID_USAGE_GENERIC_VNO ((USAGE) 0x46)
#define HID_USAGE_GENERIC_SYSCTL_POWER ((USAGE) 0x81)
#define HID_USAGE_GENERIC_SYSCTL_SLEEP ((USAGE) 0x82)
#define HID_USAGE_GENERIC_SYSCTL_WAKE ((USAGE) 0x83)
#define HID_USAGE_GENERIC_SYSCTL_CONTEXT_MENU ((USAGE) 0x84)
#define HID_USAGE_GENERIC_SYSCTL_MAIN_MENU ((USAGE) 0x85)
#define HID_USAGE_GENERIC_SYSCTL_APP_MENU ((USAGE) 0x86)
#define HID_USAGE_GENERIC_SYSCTL_HELP_MENU ((USAGE) 0x87)
#define HID_USAGE_GENERIC_SYSCTL_MENU_EXIT ((USAGE) 0x88)
#define HID_USAGE_GENERIC_SYSCTL_MENU_SELECT ((USAGE) 0x89)
#define HID_USAGE_GENERIC_SYSCTL_MENU_RIGHT ((USAGE) 0x8A)
#define HID_USAGE_GENERIC_SYSCTL_MENU_LEFT ((USAGE) 0x8B)
#define HID_USAGE_GENERIC_SYSCTL_MENU_UP ((USAGE) 0x8C)
#define HID_USAGE_GENERIC_SYSCTL_MENU_DOWN ((USAGE) 0x8D)
//
// Usages from Simulation Controls Page (0x02)
//
#define HID_USAGE_SIMULATION_RUDDER ((USAGE) 0xBA)
#define HID_USAGE_SIMULATION_THROTTLE ((USAGE) 0xBB)
//
// Virtual Reality Controls Page (0x03)
//
//
// Sport Controls Page (0x04)
//
//
// Game Controls Page (0x05)
//
//
// Keyboard/Keypad Page (0x07)
//
// Error "keys"
#define HID_USAGE_KEYBOARD_NOEVENT ((USAGE) 0x00)
#define HID_USAGE_KEYBOARD_ROLLOVER ((USAGE) 0x01)
#define HID_USAGE_KEYBOARD_POSTFAIL ((USAGE) 0x02)
#define HID_USAGE_KEYBOARD_UNDEFINED ((USAGE) 0x03)
// Letters
#define HID_USAGE_KEYBOARD_aA ((USAGE) 0x04)
#define HID_USAGE_KEYBOARD_zZ ((USAGE) 0x1D)
// Numbers
#define HID_USAGE_KEYBOARD_ONE ((USAGE) 0x1E)
#define HID_USAGE_KEYBOARD_ZERO ((USAGE) 0x27)
// Modifier Keys
#define HID_USAGE_KEYBOARD_LCTRL ((USAGE) 0xE0)
#define HID_USAGE_KEYBOARD_LSHFT ((USAGE) 0xE1)
#define HID_USAGE_KEYBOARD_LALT ((USAGE) 0xE2)
#define HID_USAGE_KEYBOARD_LGUI ((USAGE) 0xE3)
#define HID_USAGE_KEYBOARD_RCTRL ((USAGE) 0xE4)
#define HID_USAGE_KEYBOARD_RSHFT ((USAGE) 0xE5)
#define HID_USAGE_KEYBOARD_RALT ((USAGE) 0xE6)
#define HID_USAGE_KEYBOARD_RGUI ((USAGE) 0xE7)
#define HID_USAGE_KEYBOARD_SCROLL_LOCK ((USAGE) 0x47)
#define HID_USAGE_KEYBOARD_NUM_LOCK ((USAGE) 0x53)
#define HID_USAGE_KEYBOARD_CAPS_LOCK ((USAGE) 0x39)
// Funtion keys
#define HID_USAGE_KEYBOARD_F1 ((USAGE) 0x3A)
#define HID_USAGE_KEYBOARD_F12 ((USAGE) 0x45)
#define HID_USAGE_KEYBOARD_RETURN ((USAGE) 0x28)
#define HID_USAGE_KEYBOARD_ESCAPE ((USAGE) 0x29)
#define HID_USAGE_KEYBOARD_DELETE ((USAGE) 0x2A)
#define HID_USAGE_KEYBOARD_PRINT_SCREEN ((USAGE) 0x46)
// and hundreds more...
//
// LED Page (0x08)
//
#define HID_USAGE_LED_NUM_LOCK ((USAGE) 0x01)
#define HID_USAGE_LED_CAPS_LOCK ((USAGE) 0x02)
#define HID_USAGE_LED_SCROLL_LOCK ((USAGE) 0x03)
#define HID_USAGE_LED_COMPOSE ((USAGE) 0x04)
#define HID_USAGE_LED_KANA ((USAGE) 0x05)
#define HID_USAGE_LED_POWER ((USAGE) 0x06)
#define HID_USAGE_LED_SHIFT ((USAGE) 0x07)
#define HID_USAGE_LED_DO_NOT_DISTURB ((USAGE) 0x08)
#define HID_USAGE_LED_MUTE ((USAGE) 0x09)
#define HID_USAGE_LED_TONE_ENABLE ((USAGE) 0x0A)
#define HID_USAGE_LED_HIGH_CUT_FILTER ((USAGE) 0x0B)
#define HID_USAGE_LED_LOW_CUT_FILTER ((USAGE) 0x0C)
#define HID_USAGE_LED_EQUALIZER_ENABLE ((USAGE) 0x0D)
#define HID_USAGE_LED_SOUND_FIELD_ON ((USAGE) 0x0E)
#define HID_USAGE_LED_SURROUND_FIELD_ON ((USAGE) 0x0F)
#define HID_USAGE_LED_REPEAT ((USAGE) 0x10)
#define HID_USAGE_LED_STEREO ((USAGE) 0x11)
#define HID_USAGE_LED_SAMPLING_RATE_DETECT ((USAGE) 0x12)
#define HID_USAGE_LED_SPINNING ((USAGE) 0x13)
#define HID_USAGE_LED_CAV ((USAGE) 0x14)
#define HID_USAGE_LED_CLV ((USAGE) 0x15)
#define HID_USAGE_LED_RECORDING_FORMAT_DET ((USAGE) 0x16)
#define HID_USAGE_LED_OFF_HOOK ((USAGE) 0x17)
#define HID_USAGE_LED_RING ((USAGE) 0x18)
#define HID_USAGE_LED_MESSAGE_WAITING ((USAGE) 0x19)
#define HID_USAGE_LED_DATA_MODE ((USAGE) 0x1A)
#define HID_USAGE_LED_BATTERY_OPERATION ((USAGE) 0x1B)
#define HID_USAGE_LED_BATTERY_OK ((USAGE) 0x1C)
#define HID_USAGE_LED_BATTERY_LOW ((USAGE) 0x1D)
#define HID_USAGE_LED_SPEAKER ((USAGE) 0x1E)
#define HID_USAGE_LED_HEAD_SET ((USAGE) 0x1F)
#define HID_USAGE_LED_HOLD ((USAGE) 0x20)
#define HID_USAGE_LED_MICROPHONE ((USAGE) 0x21)
#define HID_USAGE_LED_COVERAGE ((USAGE) 0x22)
#define HID_USAGE_LED_NIGHT_MODE ((USAGE) 0x23)
#define HID_USAGE_LED_SEND_CALLS ((USAGE) 0x24)
#define HID_USAGE_LED_CALL_PICKUP ((USAGE) 0x25)
#define HID_USAGE_LED_CONFERENCE ((USAGE) 0x26)
#define HID_USAGE_LED_STAND_BY ((USAGE) 0x27)
#define HID_USAGE_LED_CAMERA_ON ((USAGE) 0x28)
#define HID_USAGE_LED_CAMERA_OFF ((USAGE) 0x29)
#define HID_USAGE_LED_ON_LINE ((USAGE) 0x2A)
#define HID_USAGE_LED_OFF_LINE ((USAGE) 0x2B)
#define HID_USAGE_LED_BUSY ((USAGE) 0x2C)
#define HID_USAGE_LED_READY ((USAGE) 0x2D)
#define HID_USAGE_LED_PAPER_OUT ((USAGE) 0x2E)
#define HID_USAGE_LED_PAPER_JAM ((USAGE) 0x2F)
#define HID_USAGE_LED_REMOTE ((USAGE) 0x30)
#define HID_USAGE_LED_FORWARD ((USAGE) 0x31)
#define HID_USAGE_LED_REVERSE ((USAGE) 0x32)
#define HID_USAGE_LED_STOP ((USAGE) 0x33)
#define HID_USAGE_LED_REWIND ((USAGE) 0x34)
#define HID_USAGE_LED_FAST_FORWARD ((USAGE) 0x35)
#define HID_USAGE_LED_PLAY ((USAGE) 0x36)
#define HID_USAGE_LED_PAUSE ((USAGE) 0x37)
#define HID_USAGE_LED_RECORD ((USAGE) 0x38)
#define HID_USAGE_LED_ERROR ((USAGE) 0x39)
#define HID_USAGE_LED_SELECTED_INDICATOR ((USAGE) 0x3A)
#define HID_USAGE_LED_IN_USE_INDICATOR ((USAGE) 0x3B)
#define HID_USAGE_LED_MULTI_MODE_INDICATOR ((USAGE) 0x3C)
#define HID_USAGE_LED_INDICATOR_ON ((USAGE) 0x3D)
#define HID_USAGE_LED_INDICATOR_FLASH ((USAGE) 0x3E)
#define HID_USAGE_LED_INDICATOR_SLOW_BLINK ((USAGE) 0x3F)
#define HID_USAGE_LED_INDICATOR_FAST_BLINK ((USAGE) 0x40)
#define HID_USAGE_LED_INDICATOR_OFF ((USAGE) 0x41)
#define HID_USAGE_LED_FLASH_ON_TIME ((USAGE) 0x42)
#define HID_USAGE_LED_SLOW_BLINK_ON_TIME ((USAGE) 0x43)
#define HID_USAGE_LED_SLOW_BLINK_OFF_TIME ((USAGE) 0x44)
#define HID_USAGE_LED_FAST_BLINK_ON_TIME ((USAGE) 0x45)
#define HID_USAGE_LED_FAST_BLINK_OFF_TIME ((USAGE) 0x46)
#define HID_USAGE_LED_INDICATOR_COLOR ((USAGE) 0x47)
#define HID_USAGE_LED_RED ((USAGE) 0x48)
#define HID_USAGE_LED_GREEN ((USAGE) 0x49)
#define HID_USAGE_LED_AMBER ((USAGE) 0x4A)
#define HID_USAGE_LED_GENERIC_INDICATOR ((USAGE) 0x3B)
//
// Button Page (0x09)
//
// There is no need to label these usages.
//
//
// Ordinal Page (0x0A)
//
// There is no need to label these usages.
//
//
// Telephony Device Page (0x0B)
//
#define HID_USAGE_TELEPHONY_PHONE ((USAGE) 0x01)
#define HID_USAGE_TELEPHONY_ANSWERING_MACHINE ((USAGE) 0x02)
#define HID_USAGE_TELEPHONY_MESSAGE_CONTROLS ((USAGE) 0x03)
#define HID_USAGE_TELEPHONY_HANDSET ((USAGE) 0x04)
#define HID_USAGE_TELEPHONY_HEADSET ((USAGE) 0x05)
#define HID_USAGE_TELEPHONY_KEYPAD ((USAGE) 0x06)
#define HID_USAGE_TELEPHONY_PROGRAMMABLE_BUTTON ((USAGE) 0x07)
// BUGBUG defined in ntstatus.h
#ifndef FACILITY_HID_ERROR_CODE
#define FACILITY_HID_ERROR_CODE 0x11
#endif
#define HIDP_ERROR_CODES(SEV, CODE) \
((NTSTATUS)(((SEV) << 28) | (FACILITY_HID_ERROR_CODE << 16) | (CODE)))
#define HIDP_STATUS_SUCCESS (HIDP_ERROR_CODES(0x0,0))
#define HIDP_STATUS_NULL (HIDP_ERROR_CODES(0x8,1))
#define HIDP_STATUS_INVALID_PREPARSED_DATA (HIDP_ERROR_CODES(0xC,1))
#define HIDP_STATUS_INVALID_REPORT_TYPE (HIDP_ERROR_CODES(0xC,2))
#define HIDP_STATUS_INVALID_REPORT_LENGTH (HIDP_ERROR_CODES(0xC,3))
#define HIDP_STATUS_USAGE_NOT_FOUND (HIDP_ERROR_CODES(0xC,4))
#define HIDP_STATUS_VALUE_OUT_OF_RANGE (HIDP_ERROR_CODES(0xC,5))
#define HIDP_STATUS_BAD_LOG_PHY_VALUES (HIDP_ERROR_CODES(0xC,6))
#define HIDP_STATUS_BUFFER_TOO_SMALL (HIDP_ERROR_CODES(0xC,7))
#define HIDP_STATUS_INTERNAL_ERROR (HIDP_ERROR_CODES(0xC,8))
#define HIDP_STATUS_I8242_TRANS_UNKNOWN (HIDP_ERROR_CODES(0xC,9))
#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID (HIDP_ERROR_CODES(0xC,0xA))
#define HIDP_STATUS_NOT_VALUE_ARRAY (HIDP_ERROR_CODES(0xC,0xB))
#define HIDP_STATUS_IS_VALUE_ARRAY (HIDP_ERROR_CODES(0xC,0xC))
#define HIDP_STATUS_DATA_INDEX_NOT_FOUND (HIDP_ERROR_CODES(0xC,0xD))
#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE (HIDP_ERROR_CODES(0xC,0xE))
#define HIDP_STATUS_BUTTON_NOT_PRESSED (HIDP_ERROR_CODES(0xC,0xF))
#define HIDP_STATUS_REPORT_DOES_NOT_EXIST (HIDP_ERROR_CODES(0xC,0x10))
#define HIDP_STATUS_NOT_IMPLEMENTED (HIDP_ERROR_CODES(0xC,0x20))
typedef enum _HIDP_REPORT_TYPE
{
HidP_Input,
HidP_Output,
HidP_Feature
} HIDP_REPORT_TYPE;
typedef struct _USAGE_AND_PAGE
{
USAGE Usage;
USAGE UsagePage;
} USAGE_AND_PAGE, *PUSAGE_AND_PAGE;
typedef struct _HIDP_BUTTON_CAPS
{
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection; // A unique internal index pointer
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
ULONG Reserved[10];
union {
struct {
USAGE UsageMin, UsageMax;
USHORT StringMin, StringMax;
USHORT DesignatorMin, DesignatorMax;
USHORT DataIndexMin, DataIndexMax;
} Range;
struct {
USAGE Usage, Reserved1;
USHORT StringIndex, Reserved2;
USHORT DesignatorIndex, Reserved3;
USHORT DataIndex, Reserved4;
} NotRange;
};
} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
typedef struct _HIDP_VALUE_CAPS
{
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection; // A unique internal index pointer
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
BOOLEAN HasNull; // Does this channel have a null report union
UCHAR Reserved;
USHORT BitSize; // How many bits are devoted to this value?
USHORT ReportCount; // See Note below. Usually set to 1.
USHORT Reserved2[5];
ULONG UnitsExp;
ULONG Units;
LONG LogicalMin, LogicalMax;
LONG PhysicalMin, PhysicalMax;
union {
struct {
USAGE UsageMin, UsageMax;
USHORT StringMin, StringMax;
USHORT DesignatorMin, DesignatorMax;
USHORT DataIndexMin, DataIndexMax;
} Range;
struct {
USAGE Usage, Reserved1;
USHORT StringIndex, Reserved2;
USHORT DesignatorIndex, Reserved3;
USHORT DataIndex, Reserved4;
} NotRange;
};
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
typedef struct _HIDD_ATTRIBUTES {
ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES)
//
// Vendor ids of this hid device
//
USHORT VendorID;
USHORT ProductID;
USHORT VersionNumber;
//
// Additional fields will be added to the end of this structure.
//
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
typedef PUCHAR PHIDP_REPORT_DESCRIPTOR;
typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA;
typedef struct _HIDP_CAPS
{
USAGE Usage;
USAGE UsagePage;
USHORT InputReportByteLength;
USHORT OutputReportByteLength;
USHORT FeatureReportByteLength;
USHORT Reserved[17];
USHORT NumberLinkCollectionNodes;
USHORT NumberInputButtonCaps;
USHORT NumberInputValueCaps;
USHORT NumberInputDataIndices;
USHORT NumberOutputButtonCaps;
USHORT NumberOutputValueCaps;
USHORT NumberOutputDataIndices;
USHORT NumberFeatureButtonCaps;
USHORT NumberFeatureValueCaps;
USHORT NumberFeatureDataIndices;
} HIDP_CAPS, *PHIDP_CAPS;
typedef struct _HIDP_DATA
{
USHORT DataIndex;
USHORT Reserved;
union {
ULONG RawValue; // for values
BOOLEAN On; // for buttons MUST BE TRUE for buttons.
};
} HIDP_DATA, *PHIDP_DATA;
typedef BOOLEAN(__stdcall *_HidD_GetAttributes) (HANDLE HidDeviceObject, HIDD_ATTRIBUTES *Attributes);
typedef void(__stdcall *_HidD_GetHidGuid) (GUID* HidGuid);
typedef BOOLEAN(__stdcall *_HidD_GetPreparsedData) (HANDLE HidDeviceObject, PHIDP_PREPARSED_DATA *PreparsedData);
typedef NTSTATUS(__stdcall *_HidP_GetCaps) (PHIDP_PREPARSED_DATA PreparsedData, HIDP_CAPS *caps);
typedef BOOLEAN(__stdcall *_HidD_FreePreparsedData) (PHIDP_PREPARSED_DATA PreparsedData);
typedef BOOLEAN(__stdcall *_HidD_GetFeature) (HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength);
typedef BOOLEAN(__stdcall *_HidD_SetFeature) (HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength);
typedef NTSTATUS(__stdcall *_HidP_GetSpecificButtonCaps) (HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
typedef NTSTATUS(__stdcall *_HidP_GetButtonCaps)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
typedef NTSTATUS(__stdcall *_HidP_GetUsages)(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE *UsageList, ULONG *UsageLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
typedef NTSTATUS(__stdcall *_HidP_GetValueCaps)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
typedef NTSTATUS(__stdcall *_HidP_GetUsageValue)(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
typedef BOOLEAN (__stdcall *_HidD_GetProductString)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
//#define HidP_GetButtonCaps(_Type_, _Caps_, _Len_, _Data_) \
// HidP_GetSpecificButtonCaps(_Type_, 0, 0, 0, _Caps_, _Len_, _Data_)
extern _HidD_GetHidGuid HidD_GetHidGuid;
extern _HidD_GetAttributes HidD_GetAttributes;
extern _HidD_GetPreparsedData HidD_GetPreparsedData;
extern _HidP_GetCaps HidP_GetCaps;
extern _HidD_FreePreparsedData HidD_FreePreparsedData;
extern _HidD_GetFeature HidD_GetFeature;
extern _HidD_SetFeature HidD_SetFeature;
extern _HidP_GetSpecificButtonCaps HidP_GetSpecificButtonCaps;
extern _HidP_GetButtonCaps HidP_GetButtonCaps;
extern _HidP_GetUsages HidP_GetUsages;
extern _HidP_GetValueCaps HidP_GetValueCaps;
extern _HidP_GetUsageValue HidP_GetUsageValue;
extern _HidD_GetProductString HidD_GetProductString;
void UninitHid();
int InitHid();
#include <poppack.h>
#endif

1321
pcsx2/USB/shared/inifile.cpp Normal file

File diff suppressed because it is too large Load Diff

541
pcsx2/USB/shared/inifile.h Normal file
View File

@ -0,0 +1,541 @@
/*******************************************************************************************************************************
Programmer: Ludvik Jerabek
Date: June 15th, 2009
Defined Classed: CIniFileW CIniFileA
Purpose: C++ Inifile Reader\Writer. Uses std::set and stdext::hash_set to implement an efficient ini object.
Summary: This is a total re-write of the original CIniFile class written in 2006. Originally the data structures
underlying the CIniFile object were std::list which was extreamly inefficient when dealing with huge ini files.
Note: The class currently supports std::wstring and std::string. The typedef CIniFile is based on the whether of no
_UNICODE is defined. If _UNICODE is define in your project CIniFile is a CIniFileW if _UNICODE is not defined
then CIniFile is a CIniFileA object.
Defines:
_TRACE_CINIFILE - If defined enables call tracing to standard output
_UNICODE - If defined the CIniFile will be defined as CIniFileW instead of CIniFileA
_FORCE_UNIX_LINEFEED - If defined when _WIN32 is defined (WINDOWS) the default linefeed CRLF is overridden to CR
_FORCE_WINDOWS_LINEFEED - If defined when _WIN32 is not defined (*NIX) the default linefeed CR is overridden to CRLF
Updates:
12\01\2005 - Initial MFC Release.
01\12\2006 - Ported to Ansi C++ Non-MFC.
06\16\2009 - Added support for different linefeed types, resolved issues around reading different types of linefeeds.
06\17\2009 - Added support for wide characters.
06\21\2009 - Re-written to use std::map.
07\02\2009 - Removed MFC version. Since Ansi version works in MFC ( Examples provided for download ).
07\03\2009 - Added support for VS6.
07\03\2009 - Fixed issue with SecIndexA \ SecIndexW. Were not named specific to the encoding may have caused issues.
07\03\2009 - Fixed GetKeys and GetSections functions to return const ref v.s. copy of data.
07\14\2009 - Fixed Load() whitespace preservation on key value.
09\21\2009 - Fixed removing all the sections and keys, replaced empty() with clear()
09\22\2009 - Added overloaded Load() and Save() to read\write streams
09\23\2009 - Added operators for << and >> to be used with streams
09\24\2009 - Added merge option to Load()
09\25\2009 - Added CIniMerge for use with << and >>
09\27\2009 - Moved CIniMerge into CIniFile, fixed issue with VC6 CIniFile::CR
12\29\2010 - Reduced key storage redundancy by using std::set instead of std::map
12\29\2010 - Reduced number of pass by value methods to reduce deep copy (std::string to const std::string&)
05\07\2011 - Fixed MSC_VER to _MSC_VER
05\07\2011 - Fixed OTHER file parse detection issue
*******************************************************************************************************************************/
#ifndef __CINIFILE_H_
#define __CINIFILE_H_
#ifdef _WIN32
// VC6 "identifier was truncated to '255' characters in the debug information"
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
#pragma warning(disable: 4786)
#endif
// Prevent compile time warnings for deprecation
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <set>
#include <string>
#include <string.h>
#include <wchar.h>
#include <algorithm>
#define INI_TOKEN_A_ANSI "\a" // I.E. Item1;Item2;Item3 - '\a' used in place of ';'
#define INI_TOKEN_B_ANSI "\b" // I.E. Item1,Item1b;Item2,Item2b;Item3,Item3b - '\b' used in place of ','
#define INI_EMPTY_ANSI "*" // Used to indicate empty value in token string. I.E. *;Item2;*;Item3;
class CIniFileA
{
public:
static const char* const LF;
public:
CIniFileA();
~CIniFileA();
// Used to save the data back to the file or your choice
bool Save( const std::string& fileName );
// Save data to an output stream
void Save( std::ostream& output );
// Loads the Reads the data in the ini file into the IniFile object
bool Load( const std::string& fileName , bool bMerge = false );
// Load data from an input stream
void Load( std::istream& input , bool bMerge = false );
public:
class CIniMergeA
{
public:
explicit CIniMergeA(CIniFileA& ini):_ini(ini) {}
std::istream &operator()(std::istream& input) const
{
_ini.Load( input , true );
return input;
}
private:
CIniFileA& _ini;
};
public:
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
struct ci_less_a;
#endif
#endif
class CIniSectionA
{
friend class CIniFileA; // Allow CIniFileA to create sections
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
friend struct ci_less_a;
#endif
#endif
public:
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
struct ci_less_a;
#endif
#endif
class CIniKeyA
{
friend class CIniSectionA; // Allow CIniSectionA to create keys
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
friend struct ci_less_a;
#endif
#endif
private: // CIniFileA acts as a class factory for CIniSectionA Objects
CIniKeyA( CIniSectionA* pSection , const std::string& sKeyName );
CIniKeyA( const CIniKeyA& ); // No Copy
CIniKeyA& operator=(const CIniKeyA&); // No Copy
~CIniKeyA( );
public:
// Sets the value of the key
void SetValue( const std::string& sValue );
// Returns the value of the key
std::string GetValue() const;
// Sets the key name, returns true on success, fails if the section
// name sKeyName already exists
bool SetKeyName( std::string sKeyName );
// Returns the name of the Key
std::string GetKeyName() const;
private:
// Pointer to the parent CIniSectionA
CIniSectionA* m_pSection;
// Name of the Key
std::string m_sKeyName;
// Value associated
std::string m_sValue;
}; // End of CIniKeyA
// Typedef of set of CIniKeyA pointers
struct ci_less_a
{
bool operator() (const CIniKeyA* s1, const CIniKeyA* s2) const
{
#ifndef _WIN32
return strcasecmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0;
#else
return _stricmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0;
#endif
}
};
typedef std::set<CIniKeyA*,ci_less_a> KeyIndexA;
#ifdef _WIN32
// Added for VC6 Support
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
friend class CIniKeyA;
#endif
#endif
private: // CIniSectionA acts as a class factory for CIniKeyA Objects
CIniSectionA( CIniFileA* pIniFile , const std::string& sSectionName );
CIniSectionA( const CIniSectionA& ); // No Copy
CIniSectionA& operator=(const CIniSectionA&); // No Copy
~CIniSectionA( );
public:
// Adds a key to the CIniSectionA object, returns a CIniKeyA pointer to the new or existing object
CIniKeyA* AddKey( std::string sKeyName );
// Removes a single key by pointer
void RemoveKey( CIniKeyA* pKey );
// Removes a single key by string
void RemoveKey( std::string sKey );
// Removes all the keys in the section
void RemoveAllKeys( );
// Returns a CIniKeyA pointer to the key by name, NULL if it was not found
CIniKeyA* GetKey( std::string sKeyName ) const;
// Returns all keys in the section by KeyIndex only to be used for enumeration
const KeyIndexA& GetKeys() const;
// Returns a KeyValue at a certain section
std::string GetKeyValue( std::string sKey ) const;
// Sets a KeyValuePair at a certain section
void SetKeyValue( std::string sKey, const std::string& sValue );
// Sets the section name, returns true on success, fails if the section
// name sSectionName already exists
bool SetSectionName( std::string sSectionName );
// Returns the section name
std::string GetSectionName() const;
private:
KeyIndexA::const_iterator _find_key( const std::string& sKeyName ) const;
KeyIndexA::iterator _find_key( const std::string& sKeyName );
private:
// CIniFileA pointer back to the object that instanciated the section
CIniFileA* m_pIniFile;
// Name of the section
std::string m_sSectionName;
// List of CIniKeyA pointers ( Keys in the section )
KeyIndexA m_keys;
}; // End of CIniSectionA
// Typedef of a List of CIniSectionA pointers
struct ci_less_a
{
bool operator() (const CIniSectionA* s1, const CIniSectionA* s2) const
{
#ifndef _WIN32
return strcasecmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0;
#else
return _stricmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0;
#endif
}
};
typedef std::set<CIniSectionA*,ci_less_a> SecIndexA;
#ifdef _WIN32
// Added for VC6 Support
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
friend class CIniSectionA;
#endif
#endif
public:
// Adds a section to the CIniFileA object, returns a CIniFileA pointer to the new or existing object
CIniSectionA* AddSection( std::string sSection );
// Removes section by pointer
void RemoveSection( CIniSectionA* pSection );
// Removes a section by its name sSection
void RemoveSection( std::string sSection );
// Removes all existing sections
void RemoveAllSections( );
// Returns a CIniSectionA* to the section by name, NULL if it was not found
CIniSectionA* GetSection( std::string sSection ) const;
// Returns all sections in the inifile by SecIndex, only to be used for enumeration (DO NOT KEEP THE REF OR TRY TO DELETE STUFF!)
const SecIndexA& GetSections() const;
// Returns a KeyValue at a certain section
std::string GetKeyValue( const std::string& sSection, const std::string& sKey ) const;
// Sets a KeyValuePair at a certain section
void SetKeyValue( const std::string& sSection, const std::string& sKey, const std::string& sValue );
// Renames an existing section returns true on success, false if the section didn't exist or there was another section with the same sNewSectionName
bool RenameSection( const std::string& sSectionName , const std::string& sNewSectionName );
// Renames an existing key returns true on success, false if the key didn't exist or there was another section with the same sNewSectionName
bool RenameKey( const std::string& sSectionName , const std::string& sKeyName , const std::string& sNewKeyName);
private:
SecIndexA::const_iterator _find_sec( const std::string& sSection ) const;
SecIndexA::iterator _find_sec( const std::string& sSection );
private:
CIniFileA( const CIniFileA&); // No Copy
CIniFileA& operator=(const CIniFileA&); // No Copy
// List of CIniSectionA pointers ( List of sections in the class )
SecIndexA m_sections;
}; // End of CIniFileA
// Basic typedefs for ease of use
typedef CIniFileA::CIniMergeA CIniMergeA;
typedef CIniFileA::CIniSectionA CIniSectionA;
typedef CIniSectionA::CIniKeyA CIniKeyA;
// Pointers
typedef CIniFileA* PCINIA;
typedef CIniKeyA* PCINIKEYA;
typedef CIniSectionA* PCINISECA;
// Map Types
typedef CIniSectionA::KeyIndexA KeyIndexA;
typedef CIniFileA::SecIndexA SecIndexA;
std::ostream& operator<<(std::ostream& output, CIniFileA& obj);
std::istream& operator>>(std::istream& input, CIniFileA& obj);
std::istream& operator>>(std::istream& input, CIniMergeA merger);
// Unicode Class Definition
#define INI_TOKEN_A_UNICODE L"\a" // I.E. Item1;Item2;Item3 - '\a' used in place of ';'
#define INI_TOKEN_B_UNICODE L"\b" // I.E. Item1,Item1b;Item2,Item2b;Item3,Item3b - '\b' used in place of ','
#define INI_EMPTY_UNICODE L"*" // Used to indicate empty value in token string. I.E. *;Item2;*;Item3;
class CIniFileW
{
public:
static const wchar_t* const LF;
public:
CIniFileW();
~CIniFileW();
// Used to save the data back to the file or your choice
bool Save( const std::wstring& fileName );
// Save data to an output stream
void Save( std::wostream& output );
// Loads the Reads the data in the ini file into the IniFile object
bool Load( const std::wstring& fileName , bool bMerge = false );
// Load data from an input stream
void Load( std::wistream& input , bool bMerge = false );
public:
class CIniMergeW
{
public:
explicit CIniMergeW(CIniFileW& ini):_ini(ini) {}
std::wistream &operator()(std::wistream& input) const
{
_ini.Load( input , true );
return input;
}
private:
CIniFileW& _ini;
};
public:
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
struct ci_less_w;
#endif
#endif
class CIniSectionW
{
friend class CIniFileW; // Allow CIniFileW to create sections
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
friend struct ci_less_w;
#endif
#endif
public:
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
struct ci_less_w;
#endif
#endif
class CIniKeyW
{
friend class CIniSectionW; // Allow CIniSectionW to create keys
#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
friend struct ci_less_w;
#endif
#endif
private: // CIniFileW acts as a class factory for CIniSectionW Objects
CIniKeyW( CIniSectionW* pSection , const std::wstring& sKeyName );
CIniKeyW( const CIniKeyW& ); // No Copy
CIniKeyW& operator=(const CIniKeyW&); // No Copy
~CIniKeyW( );
public:
// Sets the value of the key
void SetValue( const std::wstring& sValue );
// Returns the value of the key
std::wstring GetValue() const;
// Sets the key name, returns true on success, fails if the section
// name sKeyName already exists
bool SetKeyName( std::wstring sKeyName );
// Returns the name of the Key
std::wstring GetKeyName() const;
private:
// Pointer to the parent CIniSectionW
CIniSectionW* m_pSection;
// Name of the Key
std::wstring m_sKeyName;
// Value associated
std::wstring m_sValue;
}; // End of CIniKeyW
// Typedef of set of CIniKeyW pointers
struct ci_less_w
{
bool operator() (const CIniKeyW* s1, const CIniKeyW* s2) const
{
#ifndef _WIN32
return wcscasecmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0;
#else
return _wcsicmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0;
#endif
}
};
typedef std::set<CIniKeyW*,ci_less_w> KeyIndexW;
#ifdef _WIN32
// Added for VC6 Support
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
friend class CIniKeyW;
#endif
#endif
private: // CIniSectionW acts as a class factory for CIniKeyW Objects
CIniSectionW( CIniFileW* pIniFile , const std::wstring& sSectionName );
CIniSectionW( const CIniSectionW& ); // No Copy
CIniSectionW& operator=(const CIniSectionW&); // No Copy
~CIniSectionW( );
public:
// Adds a key to the CIniSectionW object, returns a CIniKeyW pointer to the new or existing object
CIniKeyW* AddKey( std::wstring sKeyName );
// Removes a single key by pointer
void RemoveKey( CIniKeyW* pKey );
// Removes a single key by string
void RemoveKey( std::wstring sKey );
// Removes all the keys in the section
void RemoveAllKeys( );
// Returns a CIniKeyW pointer to the key by name, NULL if it was not found
CIniKeyW* GetKey( std::wstring sKeyName ) const;
// Returns all keys in the section by KeyIndex only to be used for enumeration
const KeyIndexW& GetKeys() const;
// Returns a KeyValue at a certain section
std::wstring GetKeyValue( std::wstring sKey ) const;
// Sets a KeyValuePair at a certain section
void SetKeyValue( std::wstring sKey, const std::wstring& sValue );
// Sets the section name, returns true on success, fails if the section
// name sSectionName already exists
bool SetSectionName( std::wstring sSectionName );
// Returns the section name
std::wstring GetSectionName() const;
private:
KeyIndexW::const_iterator _find_key( const std::wstring& sKeyName ) const;
KeyIndexW::iterator _find_key( const std::wstring& sKeyName );
private:
// CIniFileW pointer back to the object that instanciated the section
CIniFileW* m_pIniFile;
// Name of the section
std::wstring m_sSectionName;
// List of CIniKeyW pointers ( Keys in the section )
KeyIndexW m_keys;
}; // End of CIniSectionW
// Typedef of a List of CIniSectionW pointers
struct ci_less_w
{
bool operator() (const CIniSectionW* s1, const CIniSectionW* s2) const
{
#ifndef _WIN32
return wcscasecmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0;
#else
return _wcsicmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0;
#endif
}
};
typedef std::set<CIniSectionW*,ci_less_w> SecIndexW;
#ifdef _WIN32
// Added for VC6 Support
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
friend class CIniSectionW;
#endif
#endif
public:
// Adds a section to the CIniFileW object, returns a CIniFileW pointer to the new or existing object
CIniSectionW* AddSection( std::wstring sSection );
// Removes section by pointer
void RemoveSection( CIniSectionW* pSection );
// Removes a section by its name sSection
void RemoveSection( std::wstring sSection );
// Removes all existing sections
void RemoveAllSections( );
// Returns a CIniSectionW* to the section by name, NULL if it was not found
CIniSectionW* GetSection( std::wstring sSection ) const;
// Returns all sections in the inifile by SecIndex, only to be used for enumeration (DO NOT KEEP THE REF OR TRY TO DELETE STUFF!)
const SecIndexW& GetSections() const;
// Returns a KeyValue at a certain section
std::wstring GetKeyValue( const std::wstring& sSection, const std::wstring& sKey ) const;
// Sets a KeyValuePair at a certain section
void SetKeyValue( const std::wstring& sSection, const std::wstring& sKey, const std::wstring& sValue );
// Renames an existing section returns true on success, false if the section didn't exist or there was another section with the same sNewSectionName
bool RenameSection( const std::wstring& sSectionName , const std::wstring& sNewSectionName );
// Renames an existing key returns true on success, false if the key didn't exist or there was another section with the same sNewSectionName
bool RenameKey( const std::wstring& sSectionName , const std::wstring& sKeyName , const std::wstring& sNewKeyName);
private:
SecIndexW::const_iterator _find_sec( const std::wstring& sSection ) const;
SecIndexW::iterator _find_sec( const std::wstring& sSection );
private:
CIniFileW( const CIniFileW&); // No Copy
CIniFileW& operator=(const CIniFileW&); // No Copy
// List of CIniSectionW pointers ( List of sections in the class )
SecIndexW m_sections;
}; // End of CIniFileW
// Basic typedefs for ease of use
typedef CIniFileW::CIniMergeW CIniMergeW;
typedef CIniFileW::CIniSectionW CIniSectionW;
typedef CIniSectionW::CIniKeyW CIniKeyW;
// Pointers
typedef CIniFileW* PCINIW;
typedef CIniKeyW* PCINIKEYW;
typedef CIniSectionW* PCINISECW;
// Map Types
typedef CIniSectionW::KeyIndexW KeyIndexW;
typedef CIniFileW::SecIndexW SecIndexW;
std::wostream& operator<<(std::wostream& output, CIniFileW& obj);
std::wistream& operator>>(std::wistream& input, CIniFileW& obj);
std::wistream& operator>>(std::wistream& input, CIniMergeW merger);
// Additional defines
#ifdef _UNICODE
#define INI_TOKEN_A INI_TOKEN_UNICODE
#define INI_TOKEN_B INI_TOKEN_UNICODE
#define INI_EMPTY INI_EMPTY_UNICODE
typedef CIniMergeW CIniMerge;
typedef CIniFileW CIniFile;
typedef CIniSectionW CIniSection;
typedef CIniKeyW CIniKey;
typedef PCINIW PCINI;
typedef PCINIKEYW PCINIKEY;
typedef PCINISECW PCINISEC;
typedef KeyIndexW KeyIndex;
typedef SecIndexW SecIndex;
#else
#define INI_TOKEN_A INI_TOKEN_ANSI
#define INI_TOKEN_B INI_TOKEN_ANSI
#define INI_EMPTY INI_EMPTY_ANSI
typedef CIniMergeA CIniMerge;
typedef CIniFileA CIniFile;
typedef CIniSectionA CIniSection;
typedef CIniKeyA CIniKey;
typedef PCINIA PCINI;
typedef PCINIKEYA PCINIKEY;
typedef PCINISECA PCINISEC;
typedef KeyIndexA KeyIndex;
typedef SecIndexA SecIndex;
#endif
#endif

View File

@ -0,0 +1,273 @@
#include "rawinput.h"
#include <cstdio>
#include <vector>
#include <algorithm>
#include "platcompat.h"
#include "osdebugout.h"
extern HINSTANCE hInst;
namespace shared{ namespace rawinput{
static std::vector<ParseRawInputCB *> callbacks;
HWND msgWindow = nullptr;
WNDPROC eatenWndProc = nullptr;
HWND eatenWnd = nullptr;
HHOOK hHook = nullptr, hHookWnd = nullptr, hHookKB = nullptr;
bool skipInput = false;
void RegisterCallback(ParseRawInputCB *cb)
{
if (cb && std::find(callbacks.begin(), callbacks.end(), cb) == callbacks.end())
callbacks.push_back(cb);
}
void UnregisterCallback(ParseRawInputCB *cb)
{
auto it = std::find(callbacks.begin(), callbacks.end(), cb);
if (it != callbacks.end())
callbacks.erase(it);
}
static POINT origCursorPos;
static POINT center;
static bool cursorCaptured = false;
static void WindowResized(HWND hWnd)
{
RECT r;
GetWindowRect(hWnd, &r);
ClipCursor(&r);
center.x = (r.left + r.right) / 2;
center.y = (r.top + r.bottom) / 2;
SetCursorPos(center.x, center.y);
}
static void CursorCapture(HWND hWnd)
{
OSDebugOut(TEXT("Capture cursor\n"));
SetCapture(hWnd);
ShowCursor(0);
GetCursorPos(&origCursorPos);
RECT r;
GetWindowRect(hWnd, &r);
ClipCursor(&r);
center.x = (r.left + r.right) / 2;
center.y = (r.top + r.bottom) / 2;
SetCursorPos(center.x, center.y);
cursorCaptured = true;
}
static void CursorRelease()
{
OSDebugOut(TEXT("Release cursor\n"));
if (cursorCaptured)
{
ClipCursor(0);
ReleaseCapture();
ShowCursor(1);
SetCursorPos(origCursorPos.x, origCursorPos.y);
cursorCaptured = false;
}
}
static void ToggleCursor(HWND hWnd, RAWKEYBOARD &k)
{
static bool shiftDown = false;
if (k.VKey == VK_SHIFT || k.VKey == VK_LSHIFT || k.VKey == VK_RSHIFT)
shiftDown = !(k.Flags & RI_KEY_BREAK);
if (shiftDown && k.VKey == VK_F11 && !k.Flags)
{
if (!cursorCaptured)
CursorCapture(hWnd);
else
CursorRelease();
}
}
static int RegisterRaw(HWND hWnd)
{
msgWindow = hWnd;
RAWINPUTDEVICE Rid[4];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = HID_USAGE_GENERIC_GAMEPAD;
Rid[0].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE; // adds game pad
Rid[0].hwndTarget = hWnd;
Rid[1].usUsagePage = 0x01;
Rid[1].usUsage = HID_USAGE_GENERIC_JOYSTICK;
Rid[1].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE; // adds joystick
Rid[1].hwndTarget = hWnd;
Rid[2].usUsagePage = 0x01;
Rid[2].usUsage = HID_USAGE_GENERIC_KEYBOARD;
Rid[2].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE;// | RIDEV_NOLEGACY; // adds HID keyboard //and also !ignores legacy keyboard messages
Rid[2].hwndTarget = hWnd;
Rid[3].usUsagePage = 0x01;
Rid[3].usUsage = HID_USAGE_GENERIC_MOUSE;
Rid[3].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE;
Rid[3].hwndTarget = hWnd;
if (RegisterRawInputDevices(Rid, countof(Rid), sizeof(Rid[0])) == FALSE) {
//registration failed. Call GetLastError for the cause of the error.
fprintf(stderr, "Could not (de)register raw input devices.\n");
return 0;
}
return 1;
}
static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_ACTIVATE:
OSDebugOut(TEXT("****** WM_ACTIVATE ****** %p %d\n"), hWnd, LOWORD(wParam) != WA_INACTIVE);
skipInput = LOWORD(wParam) == WA_INACTIVE;
break;
case WM_SETFOCUS:
OSDebugOut(TEXT("****** WM_SETFOCUS ****** %p\n"), hWnd);
skipInput = false;
break;
case WM_KILLFOCUS:
OSDebugOut(TEXT("****** WM_KILLFOCUS ****** %p\n"), hWnd);
skipInput = true;
break;
}
return 0;
}
static LRESULT CALLBACK RawInputProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PRAWINPUT pRawInput;
UINT bufferSize=0;
switch(uMsg) {
case WM_CREATE:
if (eatenWnd == nullptr)
RegisterRaw(hWnd);
break;
case WM_INPUT: {
if (skipInput) break;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
pRawInput = (PRAWINPUT)malloc(bufferSize);
if (!pRawInput)
break;
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, pRawInput, &bufferSize, sizeof(RAWINPUTHEADER)) > 0) {
if (pRawInput->header.dwType == RIM_TYPEKEYBOARD)
ToggleCursor(hWnd, pRawInput->data.keyboard);
for (auto cb : callbacks)
cb->ParseRawInput(pRawInput);
}
free(pRawInput);
break;
}
case WM_ACTIVATE:
OSDebugOut(TEXT("****** WM_ACTIVATE ****** %p %d\n"), hWnd, LOWORD(wParam) != WA_INACTIVE);
skipInput = LOWORD(wParam) == WA_INACTIVE;
if (LOWORD(wParam) == WA_INACTIVE)
CursorRelease();
break;
case WM_SETFOCUS:
OSDebugOut(TEXT("****** WM_SETFOCUS ****** %p\n"), hWnd);
//skipInput = false; //TODO when the hell is WM_SETFOCUS sent? seems like only when mouse is capped
break;
case WM_KILLFOCUS:
OSDebugOut(TEXT("****** WM_KILLFOCUS ****** %p\n"), hWnd);
//skipInput = true;
break;
case WM_SIZE:
if (cursorCaptured)
WindowResized(hWnd);
break;
case WM_DESTROY:
if (eatenWnd == nullptr)
RegisterRaw(nullptr);
Uninitialize();
break;
}
if(eatenWndProc)
return CallWindowProc(eatenWndProc, hWnd, uMsg, wParam, lParam);
//else
// return DefWindowProc(hWnd, uMsg, wParam, lParam);
return 0;
}
static LRESULT CALLBACK HookProc(INT code, WPARAM wParam, LPARAM lParam)
{
MSG *msg = reinterpret_cast<MSG*> (lParam);
//fprintf(stderr, "hook: %d, %d, %d\n", code, wParam, lParam);
if(code == HC_ACTION)
RawInputProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
return CallNextHookEx(hHook, code, wParam, lParam);
}
static LRESULT CALLBACK HookWndProc(INT code, WPARAM wParam, LPARAM lParam)
{
MSG *msg = reinterpret_cast<MSG*> (lParam);
//fprintf(stderr, "hook: %d, %d, %d\n", code, wParam, lParam);
if (code == HC_ACTION)
MyWndProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
return CallNextHookEx(hHookWnd, code, wParam, lParam);
}
static LRESULT CALLBACK KBHookProc(INT code, WPARAM wParam, LPARAM lParam)
{
fprintf(stderr, "kb hook: %d, %zd, %zd\n", code, wParam, lParam);
KBDLLHOOKSTRUCT *kb = reinterpret_cast<KBDLLHOOKSTRUCT*> (lParam);
//if(code == HC_ACTION)
// RawInputProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
return CallNextHookEx(0, code, wParam, lParam);
}
int Initialize(void *ptr)
{
HWND hWnd = reinterpret_cast<HWND> (ptr);
if (!InitHid())
return 0;
#if 0
if (!RegisterRaw(hWnd))
return 0;
hHook = SetWindowsHookEx(WH_GETMESSAGE, HookProc, hInst, 0);
//hHookWnd = SetWindowsHookEx(WH_CALLWNDPROC, HookWndProc, hInst, 0);
//hHookKB = SetWindowsHookEx(WH_KEYBOARD_LL, KBHookProc, hInst, 0);
//int err = GetLastError();
#else
eatenWnd = hWnd;
eatenWndProc = (WNDPROC) SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)RawInputProc);
RegisterRaw(hWnd);
#endif
return 1;
}
void Uninitialize()
{
if(hHook)
{
UnhookWindowsHookEx(hHook);
//UnhookWindowsHookEx(hHookKB);
hHook = 0;
}
if(eatenWnd)
RegisterRaw(nullptr);
if(eatenWnd && eatenWndProc)
SetWindowLongPtr(eatenWnd, GWLP_WNDPROC, (LONG_PTR)eatenWndProc);
eatenWndProc = nullptr;
eatenWnd = nullptr;
}
}} //namespace

View File

@ -0,0 +1,19 @@
#pragma once
#include <windows.h>
#include <setupapi.h>
#include "hidapi.h"
namespace shared{ namespace rawinput{
class ParseRawInputCB
{
public:
virtual void ParseRawInput(PRAWINPUT pRawInput) = 0;
};
int Initialize(void *hWnd);
void Uninitialize();
void RegisterCallback(ParseRawInputCB *cb);
void UnregisterCallback(ParseRawInputCB *cb);
}}

View File

@ -0,0 +1,190 @@
#include "ringbuffer.h"
#include <cstring>
#include <cassert>
#include "osdebugout.h"
#include "platcompat.h"
#if 0
#define DPRINTF OSDebugOut
#else
#define DPRINTF(...) do{}while(0)
#endif
RingBuffer::RingBuffer()
: m_begin(0)
, m_end(0)
, m_capacity(0)
, m_data(nullptr)
, m_overrun(false)
{
}
RingBuffer::RingBuffer(size_t capacity) : RingBuffer()
{
reserve(capacity);
}
RingBuffer::~RingBuffer()
{
delete [] m_data;
}
void RingBuffer::reserve(size_t capacity)
{
delete [] m_data;
m_data = new char[capacity];
memset(m_data, 0, capacity);
m_capacity = capacity;
DPRINTF(TEXT("RingBuffer %p m_data %p\n"), this, m_data);
}
size_t RingBuffer::size() const
{
size_t size = 0;
if (m_begin == m_end)
{
if (m_overrun)
size = m_capacity;
else
size = 0;
}
else if (m_begin < m_end)
size = m_end - m_begin; // [ b...e ]
else
size = m_capacity - m_begin + m_end; // [...e b...]
DPRINTF(TEXT("size %zu\n"), size);
return size;
}
size_t RingBuffer::read(uint8_t *dst, size_t nbytes)
{
size_t to_read = nbytes;
while (to_read > 0 && size() > 0)
{
size_t bytes = std::min(to_read, peek_read());
memcpy(dst, front(), bytes);
read(bytes);
dst += bytes;
to_read -= bytes;
}
return nbytes - to_read;
}
void RingBuffer::write(uint8_t *src, size_t nbytes)
{
size_t bytes;
while (nbytes > 0)
{
bytes = std::min(nbytes, m_capacity - m_end);
memcpy(back(), src, bytes);
write(bytes);
src += bytes;
nbytes -= bytes;
}
}
size_t RingBuffer::peek_write(bool overwrite) const
{
size_t peek = 0;
if (overwrite)
return m_capacity - m_end;
if (m_end < m_begin) // [...e b...]
peek = m_begin - m_end;
else if (m_end < m_capacity) // [ b...e ]
peek = m_capacity - m_end;
else
peek = m_begin; // [ b.......e]
DPRINTF(TEXT("peek_write %zu\n"), peek);
return peek;
}
size_t RingBuffer::peek_read() const
{
size_t peek = 0;
if (m_begin == m_end)
{
if (m_overrun)
peek = m_capacity - m_begin;
else
peek = 0;
}
else if (m_begin < m_end) // [ b...e ]
peek = m_end - m_begin;
else if (m_begin < m_capacity) // [...e b...]
peek = m_capacity - m_begin;
else
peek = m_end; // [...e b]
DPRINTF(TEXT("peek_read %zu\n"), peek);
return peek;
}
/*size_t RingBuffer::write(const char *data, size_t bytes)
{
size_t bytes_to_write;
if (m_end < m_begin)
{
bytes_to_write = std::min(m_begin - m_end, bytes);
memcpy(m_data + m_end, data, bytes_to_write);
m_end += bytes_to_write;
return bytes_to_write;
}
else
{
size_t in_bytes = bytes;
while (in_bytes > 0)
{
bytes_to_write = std::min(m_capacity - m_end, in_bytes);
if (m_end < m_begin && m_end + bytes_to_write > m_begin)
m_begin = (m_end + bytes_to_write + 1) % m_capacity;
memcpy(m_data + m_end, data, bytes_to_write);
in_bytes -= bytes_to_write;
m_end = (m_end + bytes_to_write) % m_capacity;
}
return bytes;
}
}*/
void RingBuffer::write(size_t bytes)
{
size_t before = m_end;
//assert( bytes <= m_capacity - size() );
// push m_begin forward if m_end overlaps it
if ((m_end < m_begin && m_end + bytes > m_begin) ||
m_end + bytes > m_begin + m_capacity)
{
m_overrun = true;
m_begin = (m_end + bytes) % m_capacity;
m_end = m_begin;
}
else
m_end = (m_end + bytes) % m_capacity;
mLastWrite = hrc::now();
DPRINTF(TEXT("write %zu begin %zu end %zu -> %zu\n"), bytes, m_begin, before, m_end);
}
void RingBuffer::read(size_t bytes)
{
assert( bytes <= size() );
size_t before = m_begin;
m_overrun = false;
if ((m_begin < m_end && m_begin + bytes > m_end) ||
m_begin + bytes > m_end + m_capacity)
{
m_begin = m_end = 0;
return;
}
m_begin = (m_begin + bytes) % m_capacity;
DPRINTF(TEXT("read %zu begin %zu -> %zu end %zu\n"), bytes, before, m_begin, m_end);
}

View File

@ -0,0 +1,85 @@
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <algorithm> // for std::min
#include <cstdint>
#include <chrono>
using hrc = std::chrono::high_resolution_clock;
using ms = std::chrono::milliseconds;
using us = std::chrono::microseconds;
using ns = std::chrono::nanoseconds;
using sec = std::chrono::seconds;
class RingBuffer
{
RingBuffer(RingBuffer&) = delete;
public:
RingBuffer();
RingBuffer(size_t capacity);
~RingBuffer();
//size_t write(const char *data, size_t bytes);
//size_t read(char *data, size_t bytes);
// Overwrites old data if nbytes > size()
void write(uint8_t *src, size_t nbytes);
size_t read(uint8_t *dst, size_t nbytes);
// just move pointers around
void write(size_t bytes);
void read(size_t bytes);
template<typename T>
void write(size_t samples) { write(samples * sizeof(T)); }
template<typename T>
void read(size_t samples) { read (samples * sizeof(T)); }
void reserve(size_t size);
// if you care about old data, check how much can be written
// may need to call available/write twice in case write pointer wraps
size_t peek_write(bool overwrite = false) const;
size_t peek_read() const;
template<typename T>
size_t peek_write(bool overwrite = false) const
{
return peek_write(overwrite) / sizeof(T);
}
template<typename T>
size_t peek_read() const
{
return peek_read() / sizeof(T);
}
// amount of valid data, may need to read twice
size_t size() const;
template<typename T>
size_t size() const
{
return size() / sizeof(T);
}
size_t capacity() const { return m_capacity; }
char* front(){ return m_data + m_begin; }
char* back(){ return m_data + m_end; }
template<typename T>
T* front() { return (T*)(m_data + m_begin); }
template<typename T>
T* back() { return (T*)(m_data + m_end); }
long long MilliSecsSinceLastWrite()
{
return std::chrono::duration_cast<ms>(hrc::now()-mLastWrite).count();
}
private:
bool m_overrun;
size_t m_begin, m_end, m_capacity;
char *m_data;
hrc::time_point mLastWrite = hrc::time_point(ns(0));
};
#endif

View File

@ -0,0 +1,26 @@
#include "shared.h"
#include <stdexcept>
#if defined(BUILD_RAW)
#include "rawinput.h"
#endif
namespace shared {
void Initialize(void *ptr)
{
// Keeping it simple, for now
#if defined(BUILD_RAW)
if (!shared::rawinput::Initialize(ptr))
throw std::runtime_error("Failed to initialize raw input!");
#endif
}
void Uninitialize(/*void *ptr*/)
{
#if defined(BUILD_RAW)
shared::rawinput::Uninitialize(/*ptr*/);
#endif
}
};

View File

@ -0,0 +1,5 @@
#pragma once
namespace shared {
void Initialize(void *ptr);
void Uninitialize(/*void *ptr*/);
};

View File

@ -0,0 +1,8 @@
#include "videodeviceproxy.h"
#include "cam-linux.h"
void usb_eyetoy::RegisterVideoDevice::Register()
{
auto& inst = RegisterVideoDevice::instance();
inst.Add(linux_api::APINAME, new VideoDeviceProxy<linux_api::V4L2>());
}

View File

@ -0,0 +1,8 @@
#include "videodeviceproxy.h"
#include "cam-windows.h"
void usb_eyetoy::RegisterVideoDevice::Register()
{
auto& inst = RegisterVideoDevice::instance();
inst.Add(windows_api::APINAME, new VideoDeviceProxy<windows_api::DirectShow>());
}

View File

@ -0,0 +1,469 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/videodev2.h>
#include "gtk.h"
#include "cam-linux.h"
#include "usb-eyetoy-webcam.h"
#include "jpgd/jpgd.h"
#include "jo_mpeg.h"
GtkWidget *new_combobox(const char* label, GtkWidget *vbox); // src/linux/config-gtk.cpp
#define CLEAR(x) memset(&(x), 0, sizeof(x))
namespace usb_eyetoy
{
namespace linux_api
{
static pthread_t _eyetoy_thread;
static pthread_t *eyetoy_thread = &_eyetoy_thread;
static unsigned char eyetoy_running = 0;
static int fd = -1;
buffer_t *buffers;
static unsigned int n_buffers;
static int out_buf;
static unsigned int pixelformat;
buffer_t mpeg_buffer;
std::mutex mpeg_mutex;
static int xioctl(int fh, unsigned long int request, void *arg) {
int r;
do {
r = ioctl(fh, request, arg);
} while (-1 == r && EINTR == errno);
return r;
}
static void store_mpeg_frame(unsigned char *data, unsigned int len) {
mpeg_mutex.lock();
memcpy(mpeg_buffer.start, data, len);
mpeg_buffer.length = len;
mpeg_mutex.unlock();
}
static void process_image(const unsigned char *ptr, int size) {
if (pixelformat == V4L2_PIX_FMT_YUYV) {
unsigned char *mpegData = (unsigned char*) calloc(1, 320 * 240 * 2);
int mpegLen = jo_write_mpeg(mpegData, ptr, 320, 240, JO_YUYV, JO_FLIP_X, JO_NONE);
store_mpeg_frame(mpegData, mpegLen);
free(mpegData);
} else if (pixelformat == V4L2_PIX_FMT_JPEG) {
int width, height, actual_comps;
unsigned char *rgbData = jpgd::decompress_jpeg_image_from_memory(ptr, size, &width, &height, &actual_comps, 3);
unsigned char *mpegData = (unsigned char*) calloc(1, 320 * 240 * 2);
int mpegLen = jo_write_mpeg(mpegData, rgbData, 320, 240, JO_RGB24, JO_FLIP_X, JO_NONE);
free(rgbData);
store_mpeg_frame(mpegData, mpegLen);
free(mpegData);
} else {
fprintf(stderr, "unk format %c%c%c%c\n", pixelformat, pixelformat>>8, pixelformat>>16, pixelformat>>24);
}
}
static int read_frame() {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
default:
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_DQBUF", errno, strerror(errno));
return -1;
}
}
assert(buf.index < n_buffers);
process_image((const unsigned char*) buffers[buf.index].start, buf.bytesused);
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_QBUF", errno, strerror(errno));
return -1;
}
return 1;
}
std::vector<std::string> getDevList() {
std::vector<std::string> devList;
char dev_name[64];
int fd;
struct v4l2_capability cap;
for (int index = 0; index < 64; index++) {
snprintf(dev_name, sizeof(dev_name), "/dev/video%d", index);
if ((fd = open(dev_name, O_RDONLY)) < 0) {
continue;
}
if(ioctl(fd, VIDIOC_QUERYCAP, &cap) >= 0) {
devList.push_back((char*)cap.card);
}
close(fd);
}
return devList;
}
static int v4l_open(std::string selectedDevice) {
char dev_name[64];
struct v4l2_capability cap;
fd = -1;
for (int index = 0; index < 64; index++) {
snprintf(dev_name, sizeof(dev_name), "/dev/video%d", index);
if ((fd = open(dev_name, O_RDWR | O_NONBLOCK, 0)) < 0) {
continue;
}
CLEAR(cap);
if(ioctl(fd, VIDIOC_QUERYCAP, &cap) >= 0) {
fprintf(stderr, "Camera: %s / %s\n", dev_name, (char*)cap.card);
if (!selectedDevice.empty() && strcmp(selectedDevice.c_str(), (char*)cap.card) == 0) {
goto cont;
}
}
close(fd);
fd = -1;
}
if (fd < 0) {
snprintf(dev_name, sizeof(dev_name), "/dev/video0");
fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
if (-1 == fd) {
fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_name, errno, strerror(errno));
return -1;
}
}
cont:
CLEAR(cap);
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
if (EINVAL == errno) {
fprintf(stderr, "%s is no V4L2 device\n", dev_name);
return -1;
} else {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_QUERYCAP", errno, strerror(errno));
return -1;
}
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "%s is no video capture device\n", dev_name);
return -1;
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr, "%s does not support streaming i/o\n", dev_name);
return -1;
}
struct v4l2_cropcap cropcap;
CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
struct v4l2_crop crop;
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect;
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
switch (errno) {
case EINVAL:
break;
default:
break;
}
}
}
struct v4l2_format fmt;
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 320;
fmt.fmt.pix.height = 240;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_S_FMT", errno, strerror(errno));
return -1;
}
pixelformat = fmt.fmt.pix.pixelformat;
fprintf(stderr, "VIDIOC_S_FMT res=%dx%d, fmt=%c%c%c%c\n", fmt.fmt.pix.width, fmt.fmt.pix.height,
pixelformat, pixelformat>>8, pixelformat>>16, pixelformat>>24
);
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
fprintf(stderr, "%s does not support memory mapping\n", dev_name);
return -1;
} else {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_REQBUFS", errno, strerror(errno));
return -1;
}
}
if (req.count < 2) {
fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name);
return -1;
}
buffers = (buffer_t*) calloc(req.count, sizeof(*buffers));
if (!buffers) {
fprintf(stderr, "Out of memory\n");
return -1;
}
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_QUERYBUF", errno, strerror(errno));
return -1;
}
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (MAP_FAILED == buffers[n_buffers].start) {
fprintf(stderr, "%s error %d, %s\n", "mmap", errno, strerror(errno));
return -1;
}
}
for (unsigned int i = 0; i < n_buffers; ++i) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_QBUF", errno, strerror(errno));
return -1;
}
}
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_STREAMON", errno, strerror(errno));
return -1;
}
return 0;
}
static void* v4l_thread(void *arg) {
while(eyetoy_running) {
for (;;) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval timeout = {2, 0}; // 2sec
int ret = select(fd + 1, &fds, NULL, NULL, &timeout);
if (ret < 0) {
if (errno == EINTR)
continue;
fprintf(stderr, "%s error %d, %s\n", "select", errno, strerror(errno));
break;
}
if (ret == 0) {
fprintf(stderr, "select timeout\n");
break;
}
if (read_frame())
break;
}
}
return NULL;
}
static int v4l_close() {
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) {
fprintf(stderr, "%s error %d, %s\n", "VIDIOC_STREAMOFF", errno, strerror(errno));
return -1;
}
for (unsigned int i = 0; i < n_buffers; ++i) {
if (-1 == munmap(buffers[i].start, buffers[i].length)) {
fprintf(stderr, "%s error %d, %s\n", "munmap", errno, strerror(errno));
return -1;
}
}
free(buffers);
if (-1 == close(fd)) {
fprintf(stderr, "%s error %d, %s\n", "close", errno, strerror(errno));
return -1;
}
fd = -1;
return 0;
}
void create_dummy_frame() {
const int width = 320;
const int height = 240;
const int bytesPerPixel = 3;
unsigned char *rgbData = (unsigned char*) calloc(1, width * height * bytesPerPixel);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
unsigned char *ptr = rgbData + (y*width+x) * bytesPerPixel;
ptr[0] = 255-y;
ptr[1] = y;
ptr[2] = 255-y;
}
}
unsigned char *mpegData = (unsigned char*) calloc(1, width * height * bytesPerPixel);
int mpegLen = jo_write_mpeg(mpegData, rgbData, width, height, JO_RGB24, JO_NONE, JO_NONE);
free(rgbData);
store_mpeg_frame(mpegData, mpegLen);
free(mpegData);
}
int V4L2::Open() {
mpeg_buffer.start = calloc(1, 320 * 240 * 2);
create_dummy_frame();
if (eyetoy_running) {
eyetoy_running = 0;
pthread_join(*eyetoy_thread, NULL);
v4l_close();
}
eyetoy_running = 1;
std::string selectedDevice;
LoadSetting(EyeToyWebCamDevice::TypeName(), mPort, APINAME, N_DEVICE, selectedDevice);
if (v4l_open(selectedDevice) != 0)
return -1;
pthread_create(eyetoy_thread, NULL, &v4l_thread, NULL);
return 0;
};
int V4L2::Close() {
if (eyetoy_running) {
eyetoy_running = 0;
pthread_join(*eyetoy_thread, NULL);
v4l_close();
}
return 0;
};
int V4L2::GetImage(uint8_t *buf, int len) {
mpeg_mutex.lock();
int len2 = mpeg_buffer.length;
if (len < mpeg_buffer.length) len2 = len;
memcpy(buf, mpeg_buffer.start, len2);
mpeg_mutex.unlock();
return len2;
};
static void deviceChanged(GtkComboBox *widget, gpointer data) {
*(int*) data = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
}
int GtkConfigure(int port, const char* dev_type, void *data) {
GtkWidget *ro_frame, *ro_label, *rs_hbox, *rs_label;
std::string selectedDevice;
LoadSetting(dev_type, port, APINAME, N_DEVICE, selectedDevice);
GtkWidget *dlg = gtk_dialog_new_with_buttons(
"V4L2 Settings", GTK_WINDOW(data), GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE);
gtk_window_set_default_size(GTK_WINDOW(dlg), 320, 75);
GtkWidget *dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
GtkWidget *main_hbox = gtk_hbox_new(FALSE, 5);
gtk_container_add(GTK_CONTAINER(dlg_area_box), main_hbox);
GtkWidget *right_vbox = gtk_vbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 5);
GtkWidget *rs_cb = new_combobox("Device:", right_vbox);
std::vector<std::string> devList = getDevList();
int sel_idx = 0;
for (auto idx = 0; idx < devList.size(); idx++) {
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), devList.at(idx).c_str());
if (!selectedDevice.empty() && selectedDevice == devList.at(idx)) {
gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), idx);
sel_idx = idx;
}
}
int sel_new;
g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(deviceChanged), (gpointer)&sel_new);
gtk_widget_show_all(dlg);
gint result = gtk_dialog_run(GTK_DIALOG(dlg));
int ret = RESULT_OK;
if (result == GTK_RESPONSE_OK) {
if (sel_new != sel_idx) {
if (!SaveSetting(dev_type, port, APINAME, N_DEVICE, devList.at(sel_new))) {
ret = RESULT_FAILED;
}
}
} else {
ret = RESULT_CANCELED;
}
gtk_widget_destroy(dlg);
return ret;
}
int V4L2::Configure(int port, const char *dev_type, void *data) {
return GtkConfigure(port, dev_type, data);
};
} // namespace linux_api
} // namespace usb_eyetoy

View File

@ -0,0 +1,38 @@
#include "videodev.h"
namespace usb_eyetoy
{
namespace linux_api
{
typedef struct {
void *start;
size_t length;
} buffer_t;
static const char *APINAME = "V4L2";
class V4L2 : public VideoDevice
{
public:
V4L2(int port) : mPort(port) {};
~V4L2(){};
int Open();
int Close();
int GetImage(uint8_t *buf, int len);
int Reset() { return 0; };
static const TCHAR *Name() {
return TEXT("V4L2");
}
static int Configure(int port, const char *dev_type, void *data);
int Port() { return mPort; }
void Port(int port) { mPort = port; }
private:
int mPort;
};
} // namespace linux_api
} // namespace usb_eyetoy

View File

@ -0,0 +1,461 @@
#include "videodev.h"
#include "cam-windows.h"
#include "usb-eyetoy-webcam.h"
#include "jo_mpeg.h"
#include "../Win32/Config.h"
#include "../Win32/resource.h"
namespace usb_eyetoy
{
namespace windows_api
{
HRESULT DirectShow::CallbackHandler::SampleCB(double time, IMediaSample *sample) {
HRESULT hr;
unsigned char* buffer;
hr = sample->GetPointer((BYTE**)&buffer);
if (hr != S_OK) return S_OK;
if (callback) callback(buffer, sample->GetActualDataLength(), BITS_PER_PIXEL);
return S_OK;
}
HRESULT DirectShow::CallbackHandler::QueryInterface(REFIID iid, LPVOID *ppv) {
if( iid == IID_ISampleGrabberCB || iid == IID_IUnknown ) {
*ppv = (void *) static_cast<ISampleGrabberCB*>( this );
return S_OK;
}
return E_NOINTERFACE;
}
std::vector<std::wstring> getDevList() {
std::vector<std::wstring> devList;
ICreateDevEnum *pCreateDevEnum = 0;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum));
if (FAILED(hr)) {
fprintf(stderr, "Error Creating Device Enumerator");
return devList;
}
IEnumMoniker *pEnum = 0;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, NULL);
if (FAILED(hr)) {
fprintf(stderr, "You have no video capture hardware");
return devList;
};
IMoniker *pMoniker = NULL;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
IPropertyBag *pPropBag;
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr)) {
pMoniker->Release();
continue;
}
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr)) {
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (SUCCEEDED(hr)) {
devList.push_back(var.bstrVal);
VariantClear(&var);
}
pPropBag->Release();
pMoniker->Release();
}
pEnum->Release();
CoUninitialize();
return devList;
}
int DirectShow::InitializeDevice(std::wstring selectedDevice) {
// Create the Capture Graph Builder.
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraphBuilder));
if (FAILED(hr)) {
fprintf(stderr, "CoCreateInstance CLSID_CaptureGraphBuilder2 err : %x\n", hr);
return -1;
}
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph));
if (FAILED(hr)) {
fprintf(stderr, "CoCreateInstance CLSID_FilterGraph err : %x\n", hr);
return -1;
}
hr = pGraphBuilder->SetFiltergraph(pGraph);
if (FAILED(hr)) {
fprintf(stderr, "SetFiltergraph err : %x\n", hr);
return -1;
}
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
if (FAILED(hr)) {
fprintf(stderr, "QueryInterface IID_IMediaControl err : %x\n", hr);
return -1;
}
// enumerate all video capture devices
ICreateDevEnum *pCreateDevEnum = 0;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum));
if (FAILED(hr)) {
fprintf(stderr, "Error Creating Device Enumerator");
return -1;
}
IEnumMoniker *pEnum = 0;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, NULL);
if (FAILED(hr)) {
fprintf(stderr, "You have no video capture hardware");
return -1;
};
pEnum->Reset();
IMoniker *pMoniker;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK && sourcefilter == NULL) {
IPropertyBag *pPropBag = 0;
hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr)) {
fprintf(stderr, "BindToStorage err : %x\n", hr);
goto freeMoniker;
}
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr)) {
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (FAILED(hr)) {
fprintf(stderr, "Read name err : %x\n", hr);
goto freeVar;
}
fprintf(stderr, "Camera: '%ls'\n", var.bstrVal);
if (!selectedDevice.empty() && selectedDevice != var.bstrVal) {
goto freeVar;
}
//add a filter for the device
hr = pGraph->AddSourceFilterForMoniker(pMoniker, NULL, L"sourcefilter", &sourcefilter);
if (FAILED(hr)) {
fprintf(stderr, "AddSourceFilterForMoniker err : %x\n", hr);
goto freeVar;
}
hr = pGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, IID_IAMStreamConfig, (void **)&pSourceConfig);
if (SUCCEEDED(hr)) {
int iCount = 0, iSize = 0;
hr = pSourceConfig->GetNumberOfCapabilities(&iCount, &iSize);
// Check the size to make sure we pass in the correct structure.
if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
// Use the video capabilities structure.
for (int iFormat = 0; iFormat < iCount; iFormat++) {
VIDEO_STREAM_CONFIG_CAPS scc;
AM_MEDIA_TYPE *pmtConfig;
hr = pSourceConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
fprintf(stderr, "GetStreamCaps min=%dx%d max=%dx%d, fmt=%x\n",
scc.MinOutputSize.cx, scc.MinOutputSize.cy,
scc.MaxOutputSize.cx, scc.MaxOutputSize.cy,
pmtConfig->subtype);
if (SUCCEEDED(hr)) {
if ((pmtConfig->majortype == MEDIATYPE_Video) &&
(pmtConfig->formattype == FORMAT_VideoInfo) &&
(pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(pmtConfig->pbFormat != NULL)) {
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
pVih->bmiHeader.biWidth = 320;
pVih->bmiHeader.biHeight = 240;
pVih->bmiHeader.biSizeImage = DIBSIZE(pVih->bmiHeader);
hr = pSourceConfig->SetFormat(pmtConfig);
}
//DeleteMediaType(pmtConfig);
}
}
}
}
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&samplegrabberfilter));
if (FAILED(hr)) {
fprintf(stderr, "CoCreateInstance CLSID_SampleGrabber err : %x\n", hr);
goto freeVar;
}
hr = pGraph->AddFilter(samplegrabberfilter, L"samplegrabberfilter");
if (FAILED(hr)) {
fprintf(stderr, "AddFilter samplegrabberfilter err : %x\n", hr);
goto freeVar;
}
//set mediatype on the samplegrabber
hr = samplegrabberfilter->QueryInterface(IID_PPV_ARGS(&samplegrabber));
if (FAILED(hr)) {
fprintf(stderr, "QueryInterface err : %x\n", hr);
goto freeVar;
}
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = samplegrabber->SetMediaType(&mt);
if (FAILED(hr)) {
fprintf(stderr, "SetMediaType err : %x\n", hr);
goto freeVar;
}
//add the callback to the samplegrabber
hr = samplegrabber->SetCallback(callbackhandler, 0);
if (hr != S_OK) {
fprintf(stderr, "SetCallback err : %x\n", hr);
goto freeVar;
}
//set the null renderer
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&nullrenderer));
if (FAILED(hr)) {
fprintf(stderr, "CoCreateInstance CLSID_NullRenderer err : %x\n", hr);
goto freeVar;
}
hr = pGraph->AddFilter(nullrenderer, L"nullrenderer");
if (FAILED(hr)) {
fprintf(stderr, "AddFilter nullrenderer err : %x\n", hr);
goto freeVar;
}
//set the render path
hr = pGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, sourcefilter, samplegrabberfilter, nullrenderer);
if (FAILED(hr)) {
fprintf(stderr, "RenderStream err : %x\n", hr);
goto freeVar;
}
// if the stream is started, start capturing immediatly
LONGLONG start = 0, stop = MAXLONGLONG;
hr = pGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, &start, &stop, 1, 2);
if (FAILED(hr)) {
fprintf(stderr, "ControlStream err : %x\n", hr);
goto freeVar;
}
freeVar:
VariantClear(&var);
pPropBag->Release();
freeMoniker:
pMoniker->Release();
}
pEnum->Release();
if (sourcefilter == NULL) {
return -1;
}
return 0;
}
void DirectShow::Start() {
HRESULT hr = nullrenderer->Run(0);
if (FAILED(hr)) throw hr;
hr = samplegrabberfilter->Run(0);
if (FAILED(hr)) throw hr;
hr = sourcefilter->Run(0);
if (FAILED(hr)) throw hr;
}
void DirectShow::Stop() {
HRESULT hr = sourcefilter->Stop();
if (FAILED(hr)) throw hr;
hr = samplegrabberfilter->Stop();
if (FAILED(hr)) throw hr;
hr = nullrenderer->Stop();
if (FAILED(hr)) throw hr;
}
buffer_t mpeg_buffer {};
std::mutex mpeg_mutex;
void store_mpeg_frame(unsigned char *data, unsigned int len) {
mpeg_mutex.lock();
memcpy(mpeg_buffer.start, data, len);
mpeg_buffer.length = len;
mpeg_mutex.unlock();
}
void dshow_callback(unsigned char *data, int len, int bitsperpixel) {
if (bitsperpixel == 24) {
unsigned char *mpegData = (unsigned char *)calloc(1, 320 * 240 * 2);
int mpegLen = jo_write_mpeg(mpegData, data, 320, 240, JO_RGB24, JO_FLIP_X, JO_FLIP_Y);
store_mpeg_frame(mpegData, mpegLen);
free(mpegData);
} else {
fprintf(stderr, "dshow_callback: unk format: len=%d bpp=%d\n", len, bitsperpixel);
}
}
void create_dummy_frame() {
const int width = 320;
const int height = 240;
const int bytesPerPixel = 3;
unsigned char *rgbData = (unsigned char*) calloc(1, width * height * bytesPerPixel);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
unsigned char *ptr = rgbData + (y*width+x) * bytesPerPixel;
ptr[0] = 255-y;
ptr[1] = y;
ptr[2] = 255-y;
}
}
unsigned char *mpegData = (unsigned char*) calloc(1, width * height * bytesPerPixel);
int mpegLen = jo_write_mpeg(mpegData, rgbData, width, height, JO_RGB24, JO_NONE, JO_NONE);
free(rgbData);
store_mpeg_frame(mpegData, mpegLen);
free(mpegData);
}
DirectShow::DirectShow(int port) {
mPort = port;
pGraphBuilder = NULL;
pGraph = NULL;
pControl = NULL;
sourcefilter = NULL;
samplegrabberfilter = NULL;
nullrenderer = NULL;
pSourceConfig = NULL;
samplegrabber = NULL;
callbackhandler = new CallbackHandler();
CoInitialize(NULL);
}
int DirectShow::Open() {
mpeg_buffer.start = calloc(1, 320 * 240 * 2);
create_dummy_frame();
std::wstring selectedDevice;
LoadSetting(EyeToyWebCamDevice::TypeName(), Port(), APINAME, N_DEVICE, selectedDevice);
int ret = InitializeDevice(selectedDevice);
if (ret < 0) {
fprintf(stderr, "Camera: cannot find '%ls'\n", selectedDevice.c_str());
return -1;
}
pControl->Run();
this->Stop();
this->SetCallback(dshow_callback);
this->Start();
return 0;
};
int DirectShow::Close() {
if (sourcefilter != NULL) {
this->Stop();
pControl->Stop();
sourcefilter->Release();
pSourceConfig->Release();
samplegrabberfilter->Release();
samplegrabber->Release();
nullrenderer->Release();
}
pGraphBuilder->Release();
pGraph->Release();
pControl->Release();
if (mpeg_buffer.start != NULL) {
free(mpeg_buffer.start);
mpeg_buffer.start = NULL;
}
return 0;
};
int DirectShow::GetImage(uint8_t *buf, int len) {
mpeg_mutex.lock();
int len2 = mpeg_buffer.length;
if (len < mpeg_buffer.length) len2 = len;
memcpy(buf, mpeg_buffer.start, len2);
mpeg_mutex.unlock();
return len2;
};
BOOL CALLBACK DirectShowDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) {
int port;
switch (uMsg) {
case WM_CREATE:
SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam);
break;
case WM_INITDIALOG: {
port = (int)lParam;
SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam);
std::wstring selectedDevice;
LoadSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice);
SendDlgItemMessage(hW, IDC_COMBO1, CB_RESETCONTENT, 0, 0);
std::vector<std::wstring> devList = getDevList();
for (auto i = 0; i != devList.size(); i++) {
SendDlgItemMessageW(hW, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)devList[i].c_str());
if (selectedDevice == devList.at(i)) {
SendDlgItemMessage(hW, IDC_COMBO1, CB_SETCURSEL, i, i);
}
}
return TRUE;
}
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case IDOK: {
INT_PTR res = RESULT_OK;
static wchar_t selectedDevice[500] = {0};
GetWindowTextW(GetDlgItem(hW, IDC_COMBO1), selectedDevice, countof(selectedDevice));
port = (int)GetWindowLongPtr(hW, GWLP_USERDATA);
if (!SaveSetting<std::wstring>(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice)) {
res = RESULT_FAILED;
}
EndDialog(hW, res);
return TRUE;
}
case IDCANCEL:
EndDialog(hW, RESULT_CANCELED);
return TRUE;
}
}
}
return FALSE;
}
int DirectShow::Configure(int port, const char *dev_type, void *data) {
Win32Handles handles = *(Win32Handles *)data;
return DialogBoxParam(handles.hInst,
MAKEINTRESOURCE(IDD_DLG_EYETOY),
handles.hWnd,
(DLGPROC)DirectShowDlgProc, port);
};
} // namespace windows_api
} // namespace usb_eyetoy

View File

@ -0,0 +1,122 @@
#include "videodev.h"
#include <mutex>
#pragma comment(lib, "strmiids")
#include <windows.h>
#include <dshow.h>
extern "C" {
extern GUID IID_ISampleGrabberCB;
extern GUID CLSID_SampleGrabber;
extern GUID CLSID_NullRenderer;
}
#pragma region qedit.h
struct __declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85"))
ISampleGrabberCB : IUnknown {
virtual HRESULT __stdcall SampleCB (double SampleTime, struct IMediaSample *pSample) = 0;
virtual HRESULT __stdcall BufferCB (double SampleTime, unsigned char *pBuffer, long BufferLen) = 0;
};
struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f"))
ISampleGrabber : IUnknown {
virtual HRESULT __stdcall SetOneShot (long OneShot) = 0;
virtual HRESULT __stdcall SetMediaType (struct _AMMediaType *pType) = 0;
virtual HRESULT __stdcall GetConnectedMediaType (struct _AMMediaType *pType) = 0;
virtual HRESULT __stdcall SetBufferSamples (long BufferThem) = 0;
virtual HRESULT __stdcall GetCurrentBuffer (long *pBufferSize, long *pBuffer) = 0;
virtual HRESULT __stdcall GetCurrentSample (struct IMediaSample **ppSample) = 0;
virtual HRESULT __stdcall SetCallback (struct ISampleGrabberCB *pCallback, long WhichMethodToCallback) = 0;
};
struct __declspec(uuid("c1f400a0-3f08-11d3-9f0b-006008039e37"))
SampleGrabber;
#pragma endregion
#ifndef MAXLONGLONG
#define MAXLONGLONG 0x7FFFFFFFFFFFFFFF
#endif
#ifndef MAX_DEVICE_NAME
#define MAX_DEVICE_NAME 80
#endif
#ifndef BITS_PER_PIXEL
#define BITS_PER_PIXEL 24
#endif
namespace usb_eyetoy
{
namespace windows_api
{
typedef void (*DShowVideoCaptureCallback)(unsigned char *data, int len, int bitsperpixel);
typedef struct {
void *start = NULL;
size_t length = 0;
} buffer_t;
static const char *APINAME = "DirectShow";
class DirectShow : public VideoDevice {
public:
DirectShow(int port);
~DirectShow() {}
int Open();
int Close();
int GetImage(uint8_t *buf, int len);
int Reset() { return 0; };
static const TCHAR *Name() {
return TEXT("DirectShow");
}
static int Configure(int port, const char *dev_type, void *data);
int Port() { return mPort; }
void Port(int port) { mPort = port; }
protected:
void SetCallback(DShowVideoCaptureCallback cb) { callbackhandler->SetCallback(cb); }
void Start();
void Stop();
int InitializeDevice(std::wstring selectedDevice);
private:
int mPort;
ICaptureGraphBuilder2 *pGraphBuilder;
IFilterGraph2 *pGraph;
IMediaControl *pControl;
IBaseFilter *sourcefilter;
IAMStreamConfig *pSourceConfig;
IBaseFilter *samplegrabberfilter;
ISampleGrabber *samplegrabber;
IBaseFilter *nullrenderer;
class CallbackHandler : public ISampleGrabberCB
{
public:
CallbackHandler() { callback = 0; }
~CallbackHandler() {}
void SetCallback(DShowVideoCaptureCallback cb) { callback = cb; }
virtual HRESULT __stdcall SampleCB(double time, IMediaSample *sample);
virtual HRESULT __stdcall BufferCB(double time, BYTE *buffer, long len) { return S_OK; }
virtual HRESULT __stdcall QueryInterface(REFIID iid, LPVOID *ppv);
virtual ULONG __stdcall AddRef() { return 1; }
virtual ULONG __stdcall Release() { return 2; }
private:
DShowVideoCaptureCallback callback;
} * callbackhandler;
};
} // namespace windows_api
} // namespace usb_eyetoy

View File

@ -0,0 +1,296 @@
/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com
*
* Latest revisions:
* 1.02 (22-03-2017) Fixed AC encoding bug.
* Fixed color space bug (thx r- lyeh!)
* 1.01 (18-10-2016) warning fixes
* 1.00 (25-09-2016) initial release
*
* Basic usage:
* char *frame = new char[width*height*4]; // 4 component. RGBX format, where X is unused
* FILE *fp = fopen("foo.mpg", "wb");
* jo_write_mpeg(fp, frame, width, height, 60); // frame 0
* jo_write_mpeg(fp, frame, width, height, 60); // frame 1
* jo_write_mpeg(fp, frame, width, height, 60); // frame 2
* ...
* fclose(fp);
*
* Notes:
* Only supports 24, 25, 30, 50, or 60 fps
*
* I don't know if decoders support changing of fps, or dimensions for each frame.
* Movie players *should* support it as the spec allows it, but ...
*
* MPEG-1/2 currently has no active patents as far as I am aware.
*
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
* http://www.cs.cornell.edu/dali/api/mpegvideo-c.html
* */
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "jo_mpeg.h"
// Huffman tables
static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}};
static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}};
static const unsigned char s_jo_HTAC[32][40][2] = {
{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},},
{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}},
{{10,5},{8,8},{22,11},{40,13},{40,14}},
{{14,6},{72,9},{56,13},{38,14}},
{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}},
{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}},
{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}},
};
static const float s_jo_quantTbl[64] = {
0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f,
0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f,
0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f,
0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f,
0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f,
0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f,
0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f,
0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f,
};
static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
typedef struct {
unsigned char *buf_ptr;
int buf, cnt;
} jo_bits_t;
static void jo_writeBits(jo_bits_t *b, int value, int count) {
b->cnt += count;
b->buf |= value << (24 - b->cnt);
while(b->cnt >= 8) {
unsigned char c = (b->buf >> 16) & 255;
//putc(c, b->fp);
*(b->buf_ptr) = c & 0xff;
b->buf_ptr++;
b->buf <<= 8;
b->cnt -= 8;
}
}
static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) {
float tmp0 = *d0 + *d7;
float tmp7 = *d0 - *d7;
float tmp1 = *d1 + *d6;
float tmp6 = *d1 - *d6;
float tmp2 = *d2 + *d5;
float tmp5 = *d2 - *d5;
float tmp3 = *d3 + *d4;
float tmp4 = *d3 - *d4;
// Even part
float tmp10 = tmp0 + tmp3; // phase 2
float tmp13 = tmp0 - tmp3;
float tmp11 = tmp1 + tmp2;
float tmp12 = tmp1 - tmp2;
*d0 = tmp10 + tmp11; // phase 3
*d4 = tmp10 - tmp11;
float z1 = (tmp12 + tmp13) * 0.707106781f; // c4
*d2 = tmp13 + z1; // phase 5
*d6 = tmp13 - z1;
// Odd part
tmp10 = tmp4 + tmp5; // phase 2
tmp11 = tmp5 + tmp6;
tmp12 = tmp6 + tmp7;
// The rotator is modified from fig 4-8 to avoid extra negations.
float z5 = (tmp10 - tmp12) * 0.382683433f; // c6
float z2 = tmp10 * 0.541196100f + z5; // c2-c6
float z4 = tmp12 * 1.306562965f + z5; // c2+c6
float z3 = tmp11 * 0.707106781f; // c4
float z11 = tmp7 + z3; // phase 5
float z13 = tmp7 - z3;
*d5 = z13 + z2; // phase 6
*d3 = z13 - z2;
*d1 = z11 + z4;
*d7 = z11 - z4;
}
static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) {
for(int dataOff=0; dataOff<64; dataOff+=8) {
jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]);
}
for(int dataOff=0; dataOff<8; ++dataOff) {
jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]);
}
int Q[64];
for(int i=0; i<64; ++i) {
float v = A[i]*s_jo_quantTbl[i];
Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
}
DC = Q[0] - DC;
int aDC = DC < 0 ? -DC : DC;
int size = 0;
int tempval = aDC;
while(tempval) {
size++;
tempval >>= 1;
}
jo_writeBits(bits, htdc[size][0], htdc[size][1]);
if(DC < 0) aDC ^= (1 << size) - 1;
jo_writeBits(bits, aDC, size);
int endpos = 63;
for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ }
for(int i = 1; i <= endpos;) {
int run = 0;
while (Q[i]==0 && i<endpos) {
++run;
++i;
}
int AC = Q[i++];
int aAC = AC < 0 ? -AC : AC;
int code = 0, size = 0;
if (run<32 && aAC<=40) {
code = s_jo_HTAC[run][aAC-1][0];
size = s_jo_HTAC[run][aAC-1][1];
if (AC < 0) code += 1;
}
if(!size) {
jo_writeBits(bits, 1, 6);
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 12);
} else if(AC > 127) {
jo_writeBits(bits, 0, 12);
}
code = AC & 0xFFF;
size = 12;
}
jo_writeBits(bits, code, size);
}
jo_writeBits(bits, 2, 2);
return Q[0];
}
unsigned long jo_write_mpeg(unsigned char *mpeg_buf, const unsigned char *raw, int width, int height, int format, int flipx, int flipy) {
int lastDCY = 128, lastDCCR = 128, lastDCCB = 128;
unsigned char *head = mpeg_buf;
jo_bits_t bits = {mpeg_buf};
for (int vblock = 0; vblock < (height+15)/16; vblock++) {
for (int hblock = 0; hblock < (width+15)/16; hblock++) {
if (vblock == 0 && hblock == 0) {
jo_writeBits(&bits, 0b01, 2); // macroblock_type = intra+quant
jo_writeBits(&bits, 8, 5); // quantiser_scale_code = 8
} else {
jo_writeBits(&bits, 0b1, 1); // macroblock_address_increment
jo_writeBits(&bits, 0b1, 1); // macroblock_type = intra
}
float Y[256], CBx[256], CRx[256];
float CB[64], CR[64];
if (format == JO_RGBX) {
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
if (flipx) x = width - 1 - x;
if (flipy) y = height - 1 - y;
const unsigned char *c = raw + y*width*4+x*4;
float r, g, b;
if (flipx && flipy) {
r = c[2], g = c[1], b = c[0];
} else {
r = c[0], g = c[1], b = c[2];
}
Y[i] = (0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
CRx[i] = (0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
}
} else
if (format == JO_RGB24) {
for (int i=0; i<256; ++i) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
if (flipx) x = width - 1 - x;
if (flipy) y = height - 1 - y;
const unsigned char *c = raw + y*width*3+x*3;
float r, g, b;
if (flipx && flipy) {
r = c[2], g = c[1], b = c[0];
} else {
r = c[0], g = c[1], b = c[2];
}
Y[i] = (0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16;
CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128;
CRx[i] = (0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128;
}
// Downsample Cb,Cr (420 format)
for (int i=0; i<64; ++i) {
int j =(i&7)*2 + (i&56)*4;
CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f;
CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f;
}
} else
if (format == JO_YUYV) {
for (int i=0; i<256; i+=2) {
int y = vblock*16+(i/16);
int x = hblock*16+(i&15);
x = x >= width ? width-1 : x;
y = y >= height ? height-1 : y;
if (flipx) x = width - 1 - x;
if (flipy) y = height - 1 - y;
const unsigned char *c = raw + y*width*2+x*2-2;
if (flipx) {
Y[i+1] = c[0];
CB[i/4] = c[1];
Y[i] = c[2];
CR[i/4] = c[3];
} else {
Y[i] = c[2];
CB[i/4] = c[3];
Y[i+1] = c[4];
CR[i/4] = c[5];
}
}
}
for (int k1=0; k1<2; ++k1) {
for (int k2=0; k2<2; ++k2) {
float block[64];
for (int i=0; i<64; i+=8) {
int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8;
memcpy(block+i, Y+j, 8*sizeof(Y[0]));
}
lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY);
}
}
lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB);
lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR);
}
}
jo_writeBits(&bits, 0, 7);
// End of Sequence
*(bits.buf_ptr++) = 0x00;
*(bits.buf_ptr++) = 0x00;
*(bits.buf_ptr++) = 0x01;
*(bits.buf_ptr++) = 0xb0;
return bits.buf_ptr - head;
}

View File

@ -0,0 +1,21 @@
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
JO_RGBX,
JO_RGB24,
JO_YUYV,
} jo_mpeg_format_t;
typedef enum {
JO_NONE,
JO_FLIP_X,
JO_FLIP_Y,
} jo_mpeg_flip_t;
unsigned long jo_write_mpeg(unsigned char *mpeg_buf, const unsigned char *rgbx, int width, int height, int format, int flipx, int flipy);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,351 @@
// jpgd.h - C++ class for JPEG decompression.
// Richard Geldreich <richgel99@gmail.com>
// See jpgd.cpp for license (Public Domain or Apache 2.0).
#ifndef JPEG_DECODER_H
#define JPEG_DECODER_H
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <assert.h>
#include <stdint.h>
#ifdef _MSC_VER
#define JPGD_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define JPGD_NORETURN __attribute__ ((noreturn))
#else
#define JPGD_NORETURN
#endif
#define JPGD_HUFF_TREE_MAX_LENGTH 512
#define JPGD_HUFF_CODE_SIZE_MAX_LENGTH 256
namespace jpgd
{
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef unsigned int uint;
typedef signed int int32;
// Loads a JPEG image from a memory buffer or a file.
// req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA).
// On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB).
// Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly.
// Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp.
unsigned char* decompress_jpeg_image_from_memory(const unsigned char* pSrc_data, int src_data_size, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0);
unsigned char* decompress_jpeg_image_from_file(const char* pSrc_filename, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0);
// Success/failure error codes.
enum jpgd_status
{
JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER,
JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM, JPGD_TOO_MANY_SCANS
};
// Input stream interface.
// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
class jpeg_decoder_stream
{
public:
jpeg_decoder_stream() { }
virtual ~jpeg_decoder_stream() { }
// The read() method is called when the internal input buffer is empty.
// Parameters:
// pBuf - input buffer
// max_bytes_to_read - maximum bytes that can be written to pBuf
// pEOF_flag - set this to true if at end of stream (no more bytes remaining)
// Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
// Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag) = 0;
};
// stdio FILE stream class.
class jpeg_decoder_file_stream : public jpeg_decoder_stream
{
jpeg_decoder_file_stream(const jpeg_decoder_file_stream&);
jpeg_decoder_file_stream& operator =(const jpeg_decoder_file_stream&);
FILE* m_pFile;
bool m_eof_flag, m_error_flag;
public:
jpeg_decoder_file_stream();
virtual ~jpeg_decoder_file_stream();
bool open(const char* Pfilename);
void close();
virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag);
};
// Memory stream class.
class jpeg_decoder_mem_stream : public jpeg_decoder_stream
{
const uint8* m_pSrc_data;
uint m_ofs, m_size;
public:
jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { }
jpeg_decoder_mem_stream(const uint8* pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { }
virtual ~jpeg_decoder_mem_stream() { }
bool open(const uint8* pSrc_data, uint size);
void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; }
virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag);
};
// Loads JPEG file from a jpeg_decoder_stream.
unsigned char* decompress_jpeg_image_from_stream(jpeg_decoder_stream* pStream, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0);
enum
{
JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 16384, JPGD_MAX_HEIGHT = 32768, JPGD_MAX_WIDTH = 32768
};
typedef int16 jpgd_quant_t;
typedef int16 jpgd_block_coeff_t;
class jpeg_decoder
{
public:
enum
{
cFlagBoxChromaFiltering = 1,
cFlagDisableSIMD = 2
};
// Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
// methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags = 0);
~jpeg_decoder();
// Call this method after constructing the object to begin decompression.
// If JPGD_SUCCESS is returned you may then call decode() on each scanline.
int begin_decoding();
// Returns the next scan line.
// For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
// Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
// Returns JPGD_SUCCESS if a scan line has been returned.
// Returns JPGD_DONE if all scan lines have been returned.
// Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
int decode(const void** pScan_line, uint* pScan_line_len);
inline jpgd_status get_error_code() const { return m_error_code; }
inline int get_width() const { return m_image_x_size; }
inline int get_height() const { return m_image_y_size; }
inline int get_num_components() const { return m_comps_in_frame; }
inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
// Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
inline int get_total_bytes_read() const { return m_total_bytes_read; }
private:
jpeg_decoder(const jpeg_decoder&);
jpeg_decoder& operator =(const jpeg_decoder&);
typedef void (*pDecode_block_func)(jpeg_decoder*, int, int, int);
struct huff_tables
{
bool ac_table;
uint look_up[256];
uint look_up2[256];
uint8 code_size[JPGD_HUFF_CODE_SIZE_MAX_LENGTH];
uint tree[JPGD_HUFF_TREE_MAX_LENGTH];
};
struct coeff_buf
{
uint8* pData;
int block_num_x, block_num_y;
int block_len_x, block_len_y;
int block_size;
};
struct mem_block
{
mem_block* m_pNext;
size_t m_used_count;
size_t m_size;
char m_data[1];
};
jmp_buf m_jmp_state;
uint32_t m_flags;
mem_block* m_pMem_blocks;
int m_image_x_size;
int m_image_y_size;
jpeg_decoder_stream* m_pStream;
int m_progressive_flag;
uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES];
uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
int m_comps_in_frame; // # of components in frame
int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
int m_comps_in_scan; // # of components in scan
int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
int m_spectral_start; // spectral selection start
int m_spectral_end; // spectral selection end
int m_successive_low; // successive approximation low
int m_successive_high; // successive approximation high
int m_max_mcu_x_size; // MCU's max. X size in pixels
int m_max_mcu_y_size; // MCU's max. Y size in pixels
int m_blocks_per_mcu;
int m_max_blocks_per_row;
int m_mcus_per_row, m_mcus_per_col;
int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
int m_total_lines_left; // total # lines left in image
int m_mcu_lines_left; // total # lines left in this MCU
int m_num_buffered_scanlines;
int m_real_dest_bytes_per_scan_line;
int m_dest_bytes_per_scan_line; // rounded up
int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
int m_eob_run;
int m_block_y_mcu[JPGD_MAX_COMPONENTS];
uint8* m_pIn_buf_ofs;
int m_in_buf_left;
int m_tem_flag;
uint8 m_in_buf_pad_start[64];
uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128];
uint8 m_in_buf_pad_end[64];
int m_bits_left;
uint m_bit_buf;
int m_restart_interval;
int m_restarts_left;
int m_next_restart_num;
int m_max_mcus_per_row;
int m_max_blocks_per_mcu;
int m_max_mcus_per_col;
uint m_last_dc_val[JPGD_MAX_COMPONENTS];
jpgd_block_coeff_t* m_pMCU_coefficients;
int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
uint8* m_pSample_buf;
uint8* m_pSample_buf_prev;
int m_crr[256];
int m_cbb[256];
int m_crg[256];
int m_cbg[256];
uint8* m_pScan_line_0;
uint8* m_pScan_line_1;
jpgd_status m_error_code;
int m_total_bytes_read;
bool m_ready_flag;
bool m_eof_flag;
bool m_sample_buf_prev_valid;
bool m_has_sse2;
inline int check_sample_buf_ofs(int ofs) const { assert(ofs >= 0); assert(ofs < m_max_blocks_per_row * 64); return ofs; }
void free_all_blocks();
JPGD_NORETURN void stop_decoding(jpgd_status status);
void* alloc(size_t n, bool zero = false);
void* alloc_aligned(size_t nSize, uint32_t align = 16, bool zero = false);
void word_clear(void* p, uint16 c, uint n);
void prep_in_buffer();
void read_dht_marker();
void read_dqt_marker();
void read_sof_marker();
void skip_variable_marker();
void read_dri_marker();
void read_sos_marker();
int next_marker();
int process_markers();
void locate_soi_marker();
void locate_sof_marker();
int locate_sos_marker();
void init(jpeg_decoder_stream* pStream, uint32_t flags);
void create_look_ups();
void fix_in_buffer();
void transform_mcu(int mcu_row);
coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
inline jpgd_block_coeff_t* coeff_buf_getp(coeff_buf* cb, int block_x, int block_y);
void load_next_row();
void decode_next_row();
void make_huff_table(int index, huff_tables* pH);
void check_quant_tables();
void check_huff_tables();
bool calc_mcu_block_order();
int init_scan();
void init_frame();
void process_restart();
void decode_scan(pDecode_block_func decode_block_func);
void init_progressive();
void init_sequential();
void decode_start();
void decode_init(jpeg_decoder_stream* pStream, uint32_t flags);
void H2V2Convert();
uint32_t H2V2ConvertFiltered();
void H2V1Convert();
void H2V1ConvertFiltered();
void H1V2Convert();
void H1V2ConvertFiltered();
void H1V1Convert();
void gray_convert();
void find_eoi();
inline uint get_char();
inline uint get_char(bool* pPadding_flag);
inline void stuff_char(uint8 q);
inline uint8 get_octet();
inline uint get_bits(int num_bits);
inline uint get_bits_no_markers(int numbits);
inline int huff_decode(huff_tables* pH);
inline int huff_decode(huff_tables* pH, int& extrabits);
// Clamps a value between 0-255.
static inline uint8 clamp(int i)
{
if (static_cast<uint>(i) > 255)
i = (((~i) >> 31) & 0xFF);
return static_cast<uint8>(i);
}
int decode_next_mcu_row();
static void decode_block_dc_first(jpeg_decoder* pD, int component_id, int block_x, int block_y);
static void decode_block_dc_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y);
static void decode_block_ac_first(jpeg_decoder* pD, int component_id, int block_x, int block_y);
static void decode_block_ac_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y);
};
} // namespace jpgd
#endif // JPEG_DECODER_H

View File

@ -0,0 +1,462 @@
// Copyright 2009 Intel Corporation
// All Rights Reserved
//
// Permission is granted to use, copy, distribute and prepare derivative works of this
// software for any purpose and without fee, provided, that the above copyright notice
// and this statement appear in all copies. Intel makes no representations about the
// suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS."
// INTEL SPECIFICALLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, AND ALL LIABILITY,
// INCLUDING CONSEQUENTIAL AND OTHER INDIRECT DAMAGES, FOR THE USE OF THIS SOFTWARE,
// INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PROPRIETARY RIGHTS, AND INCLUDING THE
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Intel does not
// assume any responsibility for any errors which may appear in this software nor any
// responsibility to update it.
//
// From:
// https://software.intel.com/sites/default/files/m/d/4/1/d/8/UsingIntelAVXToImplementIDCT-r1_5.pdf
// https://software.intel.com/file/29048
//
// Requires SSE
//
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include <immintrin.h>
#ifdef _MSC_VER
#define JPGD_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#else
#define JPGD_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#define BITS_INV_ACC 4
#define SHIFT_INV_ROW 16 - BITS_INV_ACC
#define SHIFT_INV_COL 1 + BITS_INV_ACC
const short IRND_INV_ROW = 1024 * (6 - BITS_INV_ACC); //1 << (SHIFT_INV_ROW-1)
const short IRND_INV_COL = 16 * (BITS_INV_ACC - 3); // 1 << (SHIFT_INV_COL-1)
const short IRND_INV_CORR = IRND_INV_COL - 1; // correction -1.0 and round
JPGD_SIMD_ALIGN(short, shortM128_one_corr[8]) = {1, 1, 1, 1, 1, 1, 1, 1};
JPGD_SIMD_ALIGN(short, shortM128_round_inv_row[8]) = {IRND_INV_ROW, 0, IRND_INV_ROW, 0, IRND_INV_ROW, 0, IRND_INV_ROW, 0};
JPGD_SIMD_ALIGN(short, shortM128_round_inv_col[8]) = {IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL, IRND_INV_COL};
JPGD_SIMD_ALIGN(short, shortM128_round_inv_corr[8])= {IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR, IRND_INV_CORR};
JPGD_SIMD_ALIGN(short, shortM128_tg_1_16[8]) = {13036, 13036, 13036, 13036, 13036, 13036, 13036, 13036}; // tg * (2<<16) + 0.5
JPGD_SIMD_ALIGN(short, shortM128_tg_2_16[8]) = {27146, 27146, 27146, 27146, 27146, 27146, 27146, 27146}; // tg * (2<<16) + 0.5
JPGD_SIMD_ALIGN(short, shortM128_tg_3_16[8]) = {-21746, -21746, -21746, -21746, -21746, -21746, -21746, -21746}; // tg * (2<<16) + 0.5
JPGD_SIMD_ALIGN(short, shortM128_cos_4_16[8]) = {-19195, -19195, -19195, -19195, -19195, -19195, -19195, -19195};// cos * (2<<16) + 0.5
//-----------------------------------------------------------------------------
// Table for rows 0,4 - constants are multiplied on cos_4_16
// w15 w14 w11 w10 w07 w06 w03 w02
// w29 w28 w25 w24 w21 w20 w17 w16
// w31 w30 w27 w26 w23 w22 w19 w18
//movq -> w05 w04 w01 w00
JPGD_SIMD_ALIGN(short, shortM128_tab_i_04[]) = {
16384, 21407, 16384, 8867,
16384, -8867, 16384, -21407, // w13 w12 w09 w08
16384, 8867, -16384, -21407, // w07 w06 w03 w02
-16384, 21407, 16384, -8867, // w15 w14 w11 w10
22725, 19266, 19266, -4520, // w21 w20 w17 w16
12873, -22725, 4520, -12873, // w29 w28 w25 w24
12873, 4520, -22725, -12873, // w23 w22 w19 w18
4520, 19266, 19266, -22725}; // w31 w30 w27 w26
// Table for rows 1,7 - constants are multiplied on cos_1_16
//movq -> w05 w04 w01 w00
JPGD_SIMD_ALIGN(short, shortM128_tab_i_17[]) = {
22725, 29692, 22725, 12299,
22725, -12299, 22725, -29692, // w13 w12 w09 w08
22725, 12299, -22725, -29692, // w07 w06 w03 w02
-22725, 29692, 22725, -12299, // w15 w14 w11 w10
31521, 26722, 26722, -6270, // w21 w20 w17 w16
17855, -31521, 6270, -17855, // w29 w28 w25 w24
17855, 6270, -31521, -17855, // w23 w22 w19 w18
6270, 26722, 26722, -31521}; // w31 w30 w27 w26
// Table for rows 2,6 - constants are multiplied on cos_2_16
//movq -> w05 w04 w01 w00
JPGD_SIMD_ALIGN(short, shortM128_tab_i_26[]) = {
21407, 27969, 21407, 11585,
21407, -11585, 21407, -27969, // w13 w12 w09 w08
21407, 11585, -21407, -27969, // w07 w06 w03 w02
-21407, 27969, 21407, -11585, // w15 w14 w11 w10
29692, 25172, 25172, -5906, // w21 w20 w17 w16
16819, -29692, 5906, -16819, // w29 w28 w25 w24
16819, 5906, -29692, -16819, // w23 w22 w19 w18
5906, 25172, 25172, -29692}; // w31 w30 w27 w26
// Table for rows 3,5 - constants are multiplied on cos_3_16
//movq -> w05 w04 w01 w00
JPGD_SIMD_ALIGN(short, shortM128_tab_i_35[]) = {
19266, 25172, 19266, 10426,
19266, -10426, 19266, -25172, // w13 w12 w09 w08
19266, 10426, -19266, -25172, // w07 w06 w03 w02
-19266, 25172, 19266, -10426, // w15 w14 w11 w10
26722, 22654, 22654, -5315, // w21 w20 w17 w16
15137, -26722, 5315, -15137, // w29 w28 w25 w24
15137, 5315, -26722, -15137, // w23 w22 w19 w18
5315, 22654, 22654, -26722}; // w31 w30 w27 w26
JPGD_SIMD_ALIGN(short, shortM128_128[8]) = { 128, 128, 128, 128, 128, 128, 128, 128 };
void idctSSEShortU8(const short *pInput, uint8_t * pOutputUB)
{
__m128i r_xmm0, r_xmm4;
__m128i r_xmm1, r_xmm2, r_xmm3, r_xmm5, r_xmm6, r_xmm7;
__m128i row0, row1, row2, row3, row4, row5, row6, row7;
short * pTab_i_04 = shortM128_tab_i_04;
short * pTab_i_26 = shortM128_tab_i_26;
//Get pointers for this input and output
pTab_i_04 = shortM128_tab_i_04;
pTab_i_26 = shortM128_tab_i_26;
//Row 1 and Row 3
r_xmm0 = _mm_load_si128((__m128i *) pInput);
r_xmm4 = _mm_load_si128((__m128i *) (&pInput[2*8]));
// *** Work on the data in xmm0
//low shuffle mask = 0xd8 = 11 01 10 00
//get short 2 and short 0 into ls 32-bits
r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8);
// copy short 2 and short 0 to all locations
r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0);
// add to those copies
r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04));
// shuffle mask = 0x55 = 01 01 01 01
// copy short 3 and short 1 to all locations
r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55);
// high shuffle mask = 0xd8 = 11 01 10 00
// get short 6 and short 4 into bit positions 64-95
// get short 7 and short 5 into bit positions 96-127
r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8);
// add to short 3 and short 1
r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16]));
// shuffle mask = 0xaa = 10 10 10 10
// copy short 6 and short 4 to all locations
r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa);
// shuffle mask = 0xaa = 11 11 11 11
// copy short 7 and short 5 to all locations
r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff);
// add to short 6 and short 4
r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8]));
// *** Work on the data in xmm4
// high shuffle mask = 0xd8 11 01 10 00
// get short 6 and short 4 into bit positions 64-95
// get short 7 and short 5 into bit positions 96-127
r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8);
// (xmm0 short 2 and short 0 plus pSi) + some constants
r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8);
r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24]));
r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0);
r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa);
r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &shortM128_tab_i_26[0]));
r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2);
r_xmm2 = r_xmm1;
r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55);
r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &shortM128_tab_i_26[8]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3);
r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff);
r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0);
r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &shortM128_tab_i_26[16]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1);
r_xmm2 = _mm_srai_epi32(r_xmm2, 12);
r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &shortM128_tab_i_26[24]));
r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6);
r_xmm6 = r_xmm5;
r_xmm0 = _mm_srai_epi32(r_xmm0, 12);
r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b);
row0 = _mm_packs_epi32(r_xmm0, r_xmm2);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7);
r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5);
r_xmm6 = _mm_srai_epi32(r_xmm6, 12);
r_xmm4 = _mm_srai_epi32(r_xmm4, 12);
r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b);
row2 = _mm_packs_epi32(r_xmm4, r_xmm6);
//Row 5 and row 7
r_xmm0 = _mm_load_si128((__m128i *) (&pInput[4*8]));
r_xmm4 = _mm_load_si128((__m128i *) (&pInput[6*8]));
r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8);
r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0);
r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04));
r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55);
r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8);
r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16]));
r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa);
r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff);
r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8]));
r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8);
r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8);
r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24]));
r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0);
r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa);
r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &shortM128_tab_i_26[0]));
r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2);
r_xmm2 = r_xmm1;
r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55);
r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &shortM128_tab_i_26[8]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3);
r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff);
r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0);
r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &shortM128_tab_i_26[16]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1);
r_xmm2 = _mm_srai_epi32(r_xmm2, 12);
r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &shortM128_tab_i_26[24]));
r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6);
r_xmm6 = r_xmm5;
r_xmm0 = _mm_srai_epi32(r_xmm0, 12);
r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b);
row4 = _mm_packs_epi32(r_xmm0, r_xmm2);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7);
r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5);
r_xmm6 = _mm_srai_epi32(r_xmm6, 12);
r_xmm4 = _mm_srai_epi32(r_xmm4, 12);
r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b);
row6 = _mm_packs_epi32(r_xmm4, r_xmm6);
//Row 4 and row 2
pTab_i_04 = shortM128_tab_i_35;
pTab_i_26 = shortM128_tab_i_17;
r_xmm0 = _mm_load_si128((__m128i *) (&pInput[3*8]));
r_xmm4 = _mm_load_si128((__m128i *) (&pInput[1*8]));
r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8);
r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0);
r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04));
r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55);
r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8);
r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16]));
r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa);
r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff);
r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8]));
r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8);
r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8);
r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24]));
r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0);
r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa);
r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &pTab_i_26[0]));
r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2);
r_xmm2 = r_xmm1;
r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55);
r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &pTab_i_26[8]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3);
r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff);
r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0);
r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &pTab_i_26[16]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1);
r_xmm2 = _mm_srai_epi32(r_xmm2, 12);
r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &pTab_i_26[24]));
r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6);
r_xmm6 = r_xmm5;
r_xmm0 = _mm_srai_epi32(r_xmm0, 12);
r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b);
row3 = _mm_packs_epi32(r_xmm0, r_xmm2);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7);
r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5);
r_xmm6 = _mm_srai_epi32(r_xmm6, 12);
r_xmm4 = _mm_srai_epi32(r_xmm4, 12);
r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b);
row1 = _mm_packs_epi32(r_xmm4, r_xmm6);
//Row 6 and row 8
r_xmm0 = _mm_load_si128((__m128i *) (&pInput[5*8]));
r_xmm4 = _mm_load_si128((__m128i *) (&pInput[7*8]));
r_xmm0 = _mm_shufflelo_epi16(r_xmm0, 0xd8);
r_xmm1 = _mm_shuffle_epi32(r_xmm0, 0);
r_xmm1 = _mm_madd_epi16(r_xmm1, *((__m128i *) pTab_i_04));
r_xmm3 = _mm_shuffle_epi32(r_xmm0, 0x55);
r_xmm0 = _mm_shufflehi_epi16(r_xmm0, 0xd8);
r_xmm3 = _mm_madd_epi16(r_xmm3, *((__m128i *) &pTab_i_04[16]));
r_xmm2 = _mm_shuffle_epi32(r_xmm0, 0xaa);
r_xmm0 = _mm_shuffle_epi32(r_xmm0, 0xff);
r_xmm2 = _mm_madd_epi16(r_xmm2, *((__m128i *) &pTab_i_04[8]));
r_xmm4 = _mm_shufflehi_epi16(r_xmm4, 0xd8);
r_xmm1 = _mm_add_epi32(r_xmm1, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_shufflelo_epi16(r_xmm4, 0xd8);
r_xmm0 = _mm_madd_epi16(r_xmm0, *((__m128i *) &pTab_i_04[24]));
r_xmm5 = _mm_shuffle_epi32(r_xmm4, 0);
r_xmm6 = _mm_shuffle_epi32(r_xmm4, 0xaa);
r_xmm5 = _mm_madd_epi16(r_xmm5, *((__m128i *) &pTab_i_26[0]));
r_xmm1 = _mm_add_epi32(r_xmm1, r_xmm2);
r_xmm2 = r_xmm1;
r_xmm7 = _mm_shuffle_epi32(r_xmm4, 0x55);
r_xmm6 = _mm_madd_epi16(r_xmm6, *((__m128i *) &pTab_i_26[8]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm3);
r_xmm4 = _mm_shuffle_epi32(r_xmm4, 0xff);
r_xmm2 = _mm_sub_epi32(r_xmm2, r_xmm0);
r_xmm7 = _mm_madd_epi16(r_xmm7, *((__m128i *) &pTab_i_26[16]));
r_xmm0 = _mm_add_epi32(r_xmm0, r_xmm1);
r_xmm2 = _mm_srai_epi32(r_xmm2, 12);
r_xmm5 = _mm_add_epi32(r_xmm5, *((__m128i *) shortM128_round_inv_row));
r_xmm4 = _mm_madd_epi16(r_xmm4, *((__m128i *) &pTab_i_26[24]));
r_xmm5 = _mm_add_epi32(r_xmm5, r_xmm6);
r_xmm6 = r_xmm5;
r_xmm0 = _mm_srai_epi32(r_xmm0, 12);
r_xmm2 = _mm_shuffle_epi32(r_xmm2, 0x1b);
row5 = _mm_packs_epi32(r_xmm0, r_xmm2);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm7);
r_xmm6 = _mm_sub_epi32(r_xmm6, r_xmm4);
r_xmm4 = _mm_add_epi32(r_xmm4, r_xmm5);
r_xmm6 = _mm_srai_epi32(r_xmm6, 12);
r_xmm4 = _mm_srai_epi32(r_xmm4, 12);
r_xmm6 = _mm_shuffle_epi32(r_xmm6, 0x1b);
row7 = _mm_packs_epi32(r_xmm4, r_xmm6);
r_xmm1 = _mm_load_si128((__m128i *) shortM128_tg_3_16);
r_xmm2 = row5;
r_xmm3 = row3;
r_xmm0 = _mm_mulhi_epi16(row5, r_xmm1);
r_xmm1 = _mm_mulhi_epi16(r_xmm1, r_xmm3);
r_xmm5 = _mm_load_si128((__m128i *) shortM128_tg_1_16);
r_xmm6 = row7;
r_xmm4 = _mm_mulhi_epi16(row7, r_xmm5);
r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm2);
r_xmm5 = _mm_mulhi_epi16(r_xmm5, row1);
r_xmm1 = _mm_adds_epi16(r_xmm1, r_xmm3);
r_xmm7 = row6;
r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm3);
r_xmm3 = _mm_load_si128((__m128i *) shortM128_tg_2_16);
r_xmm2 = _mm_subs_epi16(r_xmm2, r_xmm1);
r_xmm7 = _mm_mulhi_epi16(r_xmm7, r_xmm3);
r_xmm1 = r_xmm0;
r_xmm3 = _mm_mulhi_epi16(r_xmm3, row2);
r_xmm5 = _mm_subs_epi16(r_xmm5, r_xmm6);
r_xmm4 = _mm_adds_epi16(r_xmm4, row1);
r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm4);
r_xmm0 = _mm_adds_epi16(r_xmm0, *((__m128i *) shortM128_one_corr));
r_xmm4 = _mm_subs_epi16(r_xmm4, r_xmm1);
r_xmm6 = r_xmm5;
r_xmm5 = _mm_subs_epi16(r_xmm5, r_xmm2);
r_xmm5 = _mm_adds_epi16(r_xmm5, *((__m128i *) shortM128_one_corr));
r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm2);
//Intermediate results, needed later
__m128i temp3, temp7;
temp7 = r_xmm0;
r_xmm1 = r_xmm4;
r_xmm0 = _mm_load_si128((__m128i *) shortM128_cos_4_16);
r_xmm4 = _mm_adds_epi16(r_xmm4, r_xmm5);
r_xmm2 = _mm_load_si128((__m128i *) shortM128_cos_4_16);
r_xmm2 = _mm_mulhi_epi16(r_xmm2, r_xmm4);
//Intermediate results, needed later
temp3 = r_xmm6;
r_xmm1 = _mm_subs_epi16(r_xmm1, r_xmm5);
r_xmm7 = _mm_adds_epi16(r_xmm7, row2);
r_xmm3 = _mm_subs_epi16(r_xmm3, row6);
r_xmm6 = row0;
r_xmm0 = _mm_mulhi_epi16(r_xmm0, r_xmm1);
r_xmm5 = row4;
r_xmm5 = _mm_adds_epi16(r_xmm5, r_xmm6);
r_xmm6 = _mm_subs_epi16(r_xmm6, row4);
r_xmm4 = _mm_adds_epi16(r_xmm4, r_xmm2);
r_xmm4 = _mm_or_si128(r_xmm4, *((__m128i *) shortM128_one_corr));
r_xmm0 = _mm_adds_epi16(r_xmm0, r_xmm1);
r_xmm0 = _mm_or_si128(r_xmm0, *((__m128i *) shortM128_one_corr));
r_xmm2 = r_xmm5;
r_xmm5 = _mm_adds_epi16(r_xmm5, r_xmm7);
r_xmm1 = r_xmm6;
r_xmm5 = _mm_adds_epi16(r_xmm5, *((__m128i *) shortM128_round_inv_col));
r_xmm2 = _mm_subs_epi16(r_xmm2, r_xmm7);
r_xmm7 = temp7;
r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm3);
r_xmm6 = _mm_adds_epi16(r_xmm6, *((__m128i *) shortM128_round_inv_col));
r_xmm7 = _mm_adds_epi16(r_xmm7, r_xmm5);
r_xmm7 = _mm_srai_epi16(r_xmm7, SHIFT_INV_COL);
r_xmm1 = _mm_subs_epi16(r_xmm1, r_xmm3);
r_xmm1 = _mm_adds_epi16(r_xmm1, *((__m128i *) shortM128_round_inv_corr));
r_xmm3 = r_xmm6;
r_xmm2 = _mm_adds_epi16(r_xmm2, *((__m128i *) shortM128_round_inv_corr));
r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm4);
//Store results for row 0
//_mm_store_si128((__m128i *) pOutput, r_xmm7);
__m128i r0 = r_xmm7;
r_xmm6 = _mm_srai_epi16(r_xmm6, SHIFT_INV_COL);
r_xmm7 = r_xmm1;
r_xmm1 = _mm_adds_epi16(r_xmm1, r_xmm0);
//Store results for row 1
//_mm_store_si128((__m128i *) (&pOutput[1*8]), r_xmm6);
__m128i r1 = r_xmm6;
r_xmm1 = _mm_srai_epi16(r_xmm1, SHIFT_INV_COL);
r_xmm6 = temp3;
r_xmm7 = _mm_subs_epi16(r_xmm7, r_xmm0);
r_xmm7 = _mm_srai_epi16(r_xmm7, SHIFT_INV_COL);
//Store results for row 2
//_mm_store_si128((__m128i *) (&pOutput[2*8]), r_xmm1);
__m128i r2 = r_xmm1;
r_xmm5 = _mm_subs_epi16(r_xmm5, temp7);
r_xmm5 = _mm_srai_epi16(r_xmm5, SHIFT_INV_COL);
//Store results for row 7
//_mm_store_si128((__m128i *) (&pOutput[7*8]), r_xmm5);
__m128i r7 = r_xmm5;
r_xmm3 = _mm_subs_epi16(r_xmm3, r_xmm4);
r_xmm6 = _mm_adds_epi16(r_xmm6, r_xmm2);
r_xmm2 = _mm_subs_epi16(r_xmm2, temp3);
r_xmm6 = _mm_srai_epi16(r_xmm6, SHIFT_INV_COL);
r_xmm2 = _mm_srai_epi16(r_xmm2, SHIFT_INV_COL);
//Store results for row 3
//_mm_store_si128((__m128i *) (&pOutput[3*8]), r_xmm6);
__m128i r3 = r_xmm6;
r_xmm3 = _mm_srai_epi16(r_xmm3, SHIFT_INV_COL);
//Store results for rows 4, 5, and 6
//_mm_store_si128((__m128i *) (&pOutput[4*8]), r_xmm2);
//_mm_store_si128((__m128i *) (&pOutput[5*8]), r_xmm7);
//_mm_store_si128((__m128i *) (&pOutput[6*8]), r_xmm3);
__m128i r4 = r_xmm2;
__m128i r5 = r_xmm7;
__m128i r6 = r_xmm3;
r0 = _mm_add_epi16(*(const __m128i *)shortM128_128, r0);
r1 = _mm_add_epi16(*(const __m128i *)shortM128_128, r1);
r2 = _mm_add_epi16(*(const __m128i *)shortM128_128, r2);
r3 = _mm_add_epi16(*(const __m128i *)shortM128_128, r3);
r4 = _mm_add_epi16(*(const __m128i *)shortM128_128, r4);
r5 = _mm_add_epi16(*(const __m128i *)shortM128_128, r5);
r6 = _mm_add_epi16(*(const __m128i *)shortM128_128, r6);
r7 = _mm_add_epi16(*(const __m128i *)shortM128_128, r7);
((__m128i *)pOutputUB)[0] = _mm_packus_epi16(r0, r1);
((__m128i *)pOutputUB)[1] = _mm_packus_epi16(r2, r3);
((__m128i *)pOutputUB)[2] = _mm_packus_epi16(r4, r5);
((__m128i *)pOutputUB)[3] = _mm_packus_epi16(r6, r7);
}

View File

@ -0,0 +1,41 @@
/* I2C registers */
#define R51x_I2C_W_SID 0x41
#define R51x_I2C_SADDR_3 0x42
#define R51x_I2C_SADDR_2 0x43
#define R51x_I2C_R_SID 0x44
#define R51x_I2C_DATA 0x45
#define R518_I2C_CTL 0x47 /* OV518(+) only */
#define OVFX2_I2C_ADDR 0x00
/* OV519 Camera interface register numbers */
#define OV519_R10_H_SIZE 0x10
#define OV519_R11_V_SIZE 0x11
#define OV519_R12_X_OFFSETL 0x12
#define OV519_R13_X_OFFSETH 0x13
#define OV519_R14_Y_OFFSETL 0x14
#define OV519_R15_Y_OFFSETH 0x15
#define OV519_R16_DIVIDER 0x16
#define OV519_R20_DFR 0x20
#define OV519_R25_FORMAT 0x25
/* OV519 System Controller register numbers */
#define OV519_R51_RESET1 0x51
#define OV519_R54_EN_CLK1 0x54
#define OV519_R57_SNAPSHOT 0x57
#define OV519_GPIO_DATA_OUT0 0x71
#define OV519_GPIO_IO_CTRL0 0x72
/* OV7610 registers */
#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
#define OV7610_REG_BLUE 0x01 /* blue channel balance */
#define OV7610_REG_RED 0x02 /* red channel balance */
#define OV7610_REG_SAT 0x03 /* saturation */
#define OV8610_REG_HUE 0x04 /* 04 reserved */
#define OV7610_REG_CNT 0x05 /* Y contrast */
#define OV7610_REG_BRT 0x06 /* Y brightness */
#define OV7610_REG_COM_C 0x14 /* misc common regs */
#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */
#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */
#define OV7610_REG_COM_I 0x29 /* misc settings */

Some files were not shown because too many files have changed in this diff Show More