mirror of https://github.com/PCSX2/pcsx2.git
USB: beginning to add back initial references
This commit is contained in:
parent
9dd0ef681d
commit
8f8a83a038
|
@ -0,0 +1 @@
|
||||||
|
Erik de Castro Lopo <erikd@mega-nerd.com>
|
|
@ -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.
|
|
@ -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.
|
||||||
|
|
|
@ -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/
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#include <windows.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <propsys.h>
|
||||||
|
#include <functiondiscoverykeys_devpkey.h>
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
bool DynLoadPulse();
|
||||||
|
void DynUnloadPulse();
|
|
@ -0,0 +1,3 @@
|
||||||
|
#define GLIB_DISABLE_DEPRECATION_WARNINGS
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
|
};
|
|
@ -0,0 +1,2 @@
|
||||||
|
#pragma once
|
||||||
|
extern const unsigned char icon_buzz_24[];
|
|
@ -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()
|
|
@ -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 */
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef LINUXCONFIG_H
|
||||||
|
#define LINUXCONFIG_H
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include "osdebugout.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -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()
|
|
@ -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 */
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
bool file_exists(std::string path);
|
||||||
|
bool dir_exists(std::string path);
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
};
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
|
@ -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]);
|
|
@ -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;
|
|
@ -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;
|
||||||
|
}
|
|
@ -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) */
|
||||||
|
};
|
|
@ -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;
|
|
@ -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);
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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);
|
|
@ -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
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
}}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
namespace shared {
|
||||||
|
void Initialize(void *ptr);
|
||||||
|
void Uninitialize(/*void *ptr*/);
|
||||||
|
};
|
|
@ -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>());
|
||||||
|
}
|
|
@ -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>());
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
Loading…
Reference in New Issue