mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-12 18:45:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a6dc36b1c | ||
|
|
c6967f56bd | ||
|
|
0e654c8e9e | ||
|
|
e1270742b9 | ||
|
|
1c87853496 | ||
|
|
119788593c | ||
|
|
fb2b3695d9 | ||
|
|
aacf2bed34 | ||
|
|
badd588652 | ||
|
|
a40c3767bb | ||
|
|
b0831bf1a0 | ||
|
|
d1f7d8af0d | ||
|
|
f7f007519c | ||
|
|
92a098ee02 | ||
|
|
d00c2a6407 | ||
|
|
991b43d4ee | ||
|
|
ac8a977492 | ||
|
|
1bc588acbd | ||
|
|
eafeb83d0b | ||
|
|
2e215bda5b | ||
|
|
0a5fd837a2 | ||
|
|
edf10b576f | ||
|
|
46dcf1204e | ||
|
|
5feeb362b1 | ||
|
|
67358fb145 | ||
|
|
41fbbda8e8 | ||
|
|
bd1d39d7fb | ||
|
|
b2c7fae912 | ||
|
|
13e88ebe74 | ||
|
|
d5d6b1d91e | ||
|
|
8915d82fd6 | ||
|
|
0785d4aa72 | ||
|
|
0744683c2b | ||
|
|
55afe18872 | ||
|
|
f83e614b4e | ||
|
|
5dc070f041 | ||
|
|
585af66b5c | ||
|
|
c8241f3f21 | ||
|
|
3a82cf980d | ||
|
|
7b47f5f49a | ||
|
|
94e4b1883a | ||
|
|
c73592d379 | ||
|
|
4ba0ba4c70 | ||
|
|
11dba60ea7 | ||
|
|
97ad8cbfd2 | ||
|
|
b5be2006f2 | ||
|
|
8ec39ca7ec | ||
|
|
bd937eb49f | ||
|
|
23b810b820 | ||
|
|
322aeb4e46 | ||
|
|
ed2d6b1730 | ||
|
|
9afa30af5e | ||
|
|
5d3719df44 | ||
|
|
3f00d2f041 | ||
|
|
a30a86a09c | ||
|
|
bd10e5bdc3 | ||
|
|
0e1d5ba30e | ||
|
|
10f42d7034 | ||
|
|
6986caed5b | ||
|
|
c802a549f6 | ||
|
|
0a75b51b29 | ||
|
|
0c64ac0bde | ||
|
|
15b0f5478f | ||
|
|
a6b0199204 | ||
|
|
973f5cda6d | ||
|
|
46a318db45 | ||
|
|
7949a9c8ad | ||
|
|
2ae11b7222 | ||
|
|
f05c124c98 | ||
|
|
5f182fa564 | ||
|
|
c2d5003f80 | ||
|
|
6e2709d62a | ||
|
|
eef86d5f72 | ||
|
|
863aaa5b9d | ||
|
|
54eed63418 | ||
|
|
7695298dfc | ||
|
|
917fb1f796 | ||
|
|
81075aa5fa | ||
|
|
ff7982fbcd | ||
|
|
7915b040a9 | ||
|
|
a129764f0d | ||
|
|
aeddcb54e0 | ||
|
|
10f0073778 | ||
|
|
8b6e58eebd | ||
|
|
49bd8d2d27 | ||
|
|
946d87cf89 | ||
|
|
138d5b3a4e | ||
|
|
cdba9accdf | ||
|
|
963350a0f2 | ||
|
|
dd48b65687 | ||
|
|
642d6c9fb8 | ||
|
|
6738fa57aa | ||
|
|
d40d7c389b | ||
|
|
3c2e3f1ce9 | ||
|
|
766c59d469 | ||
|
|
7492f5d60c | ||
|
|
bd9c35d379 | ||
|
|
5d4069cf8f | ||
|
|
0caeed6e1d | ||
|
|
98194ce135 | ||
|
|
4dcda032d0 | ||
|
|
f74fddf3fd | ||
|
|
08481e2a68 | ||
|
|
dc50ff584e | ||
|
|
3647bdef0f | ||
|
|
75fcd63b4a | ||
|
|
27cacffe54 | ||
|
|
74d85e3269 | ||
|
|
9d92888846 | ||
|
|
adfabcb742 | ||
|
|
620302ddaa | ||
|
|
da9f87c63f | ||
|
|
fe2fd18aad | ||
|
|
1c61068291 | ||
|
|
681983d1b2 | ||
|
|
ec627a9c00 | ||
|
|
462cedb654 | ||
|
|
f1347829c2 | ||
|
|
b08169255d | ||
|
|
610b0b7095 | ||
|
|
caf3f3dadd | ||
|
|
04962e1741 | ||
|
|
44e2530fbb | ||
|
|
52b827155f | ||
|
|
3eecf92f8f | ||
|
|
9184a9d04c | ||
|
|
5534fd7275 | ||
|
|
8e06aa2417 | ||
|
|
f57fcd4048 | ||
|
|
4b4576ed62 | ||
|
|
9f8092dc27 | ||
|
|
e40f0d308b | ||
|
|
2259e9b38e | ||
|
|
e44f0749a1 | ||
|
|
3fe52bb7fb | ||
|
|
2edc6d6e6a | ||
|
|
fce9da5e76 | ||
|
|
4b2f27eff8 | ||
|
|
7919a87b97 | ||
|
|
8584b122e1 | ||
|
|
ec7e528094 | ||
|
|
f0c9c542a2 | ||
|
|
4ed88ec68a | ||
|
|
140b8f5e0a | ||
|
|
b5df2b883a | ||
|
|
4fab84ca9e | ||
|
|
d8a5505204 | ||
|
|
b81ec7d631 | ||
|
|
f8479c2b5a | ||
|
|
69f3dda90a | ||
|
|
a06b303879 | ||
|
|
ef9beea9f8 | ||
|
|
cd8f6ede4e | ||
|
|
e76265e1aa | ||
|
|
6443d6f019 | ||
|
|
293e797146 | ||
|
|
55c855267e | ||
|
|
73a5ec9f42 | ||
|
|
86f8670d0b | ||
|
|
6772b89872 | ||
|
|
d95e46c2bd | ||
|
|
74d9f976a8 | ||
|
|
b1a3ea72c9 | ||
|
|
ab888629a5 | ||
|
|
a8028d5e8e | ||
|
|
cc1e080dfe | ||
|
|
35f7ef3cc2 | ||
|
|
84975d71ea | ||
|
|
cea6b17391 | ||
|
|
0a83817f25 | ||
|
|
f8b4289811 |
No files matched your search
+66
@@ -1,5 +1,70 @@
|
||||
# Future
|
||||
|
||||
# 1.9.6
|
||||
- ARCHIVE: Fix archive delimiter detection when file path contains no slashes
|
||||
- ANDROID: Do not duplicate port 0 mouse and gun inputs to other ports
|
||||
- AUDIO/XAUDIO2: Fail instead of crashing when disconnecting an audio device
|
||||
- CHEEVOS: Reset cached progress each time menu is opened
|
||||
- CRT/SWITCHRES: Add support for switchres.ini core and directory overrides
|
||||
- D3D11: Don't use allow tearing flag with blit swap chains. Also disables the flip model if the allow tearing flag is not supported.
|
||||
- D3D11: Disable DXGI's ALT+ENTER handling
|
||||
- D3D11: Don't pass ALLOW_TEARING when unsupported
|
||||
- D3D11: Fix non-vsynced output without flip, black screens in fullscreen
|
||||
- D3D12: Relcoated 'd3d12_gfx_sync'
|
||||
- D3D12: Fixed swap interval option
|
||||
- GFX: Fix uninitialized variables in gfx_display_draw_cursor
|
||||
- HISTORY: Hide 'Add to Favorites' when viewing an entry of the favorites playlist
|
||||
- INPUT: 'Analog to Digital Type' usability improvements
|
||||
- INPUT: Add support for mapping multiple controllers to a single input device
|
||||
- INPUT/REMAPPING: Add support for mapping multiple controllers to a single input device
|
||||
- INPUT/LIGHTGUN: Bind lightgun trigger to first mouse button by default
|
||||
- INPUT/WINDOWS/RAWINPUT: Mouse access violation fix
|
||||
- INPUT/UDEV: Only add mouse if it has buttons and add vebose device friendly names
|
||||
- INPUT/UDEV: Skip mouse with no button errors and keep the rest
|
||||
- INPUT/UDEV: Fix Game Focus mode
|
||||
- INPUT/UDEV/X11: Change udev driver for dual lightgun support in X11
|
||||
- LIBNX/SWITCH: Update to libnx 4.0.0
|
||||
- LOCALIZATION: Fetch translations from Crowdin
|
||||
- LOCALIZATION: Fix Switchres menu texts
|
||||
- MENU/OZONE: Ensure sidebar display status is updated correctly when performing rapid menu navigation
|
||||
- MENU/XMB: Dynamic wallpaper fix
|
||||
- MENU/XMB: Icon opacity fix
|
||||
- MENU/QT/WIMP: Fix default core detection when playlist file name does not match 'db_name'
|
||||
- PLAYLISTS: Optimise scanning of large rom sets
|
||||
- SECURITY: Plug so-called high-risk vulnerability related to Powershell - avoid injection - don't send speech input as commandline argument
|
||||
- UWP/XBOX: Add expanded resources Rescap to increase performance of UWP version in app mode on Xbox
|
||||
- WINDOWS/INSTALLER: Add smarter isEmptyDir reference implementation that looks for subdirectories from NSIS documentation
|
||||
- WINDOWS/INSTALLER: Register new function DirectorySet that is called when pressing the "Next" button on the MUI_PAGE_DIRECTORY, aka the install folder selection GUI. DirectorySet contains the criteria for an acceptable folder, which are:
|
||||
- `IfFileExists "$INSTDIR\retroarch.exe"` returns 1
|
||||
- `IfFileExists "$INSTDIR\*.*` returns 0, there is no existing folder
|
||||
- `IfFileExists "$INSTDIR\*.*"` returns 1, there is a folder, and `isEmptyDir` returns 1, therefore the folder is empty, including of subdirectories
|
||||
- X11: Fix threaded video segfault
|
||||
|
||||
# 1.9.5
|
||||
- ALSATHREAD: Make alsathread default for all ALSA devices with threads
|
||||
- ARCHIVE: Fix loading of archived content with file names containing '#' characters
|
||||
- CHEEVOS: Upgrade to rcheevos 10.1
|
||||
- CHEEVOS: Challenge indicators
|
||||
- CHEEVOS: Group achievements by category in quick menu
|
||||
- CHEEVOS: Relabel 'Start Active' with 'Encore Mode'
|
||||
- D3D10: Window title should now update
|
||||
- D3D11: Window title should now update
|
||||
- D3D11: Allow fastforward in fullscreen
|
||||
- D3D12: Window title should now update
|
||||
- D3D12: Allow fastforward in fullscreen
|
||||
- CRT/SWITCHRES: New implementation
|
||||
- FONTS: Improve message wrapping with CJK languages
|
||||
- FONTS: Fix garbled characters when converting encodings
|
||||
- INPUT: Allow the 8 analog stick directions to be used as keys for core keyboard mappings
|
||||
- LIBRETRO: Add API extension for setting 'need_fullpath' based on content file extension and to request persistent frontend content data buffers
|
||||
- MENU/SEARCH: Add enhanced search functionality to the 'Manage Cores' menu
|
||||
- OPENDINGUX: Fix black screens when triggering gfx driver initialisation via menu actions
|
||||
- UNIX: Get better battery stats on sysfs nodes
|
||||
- VIDEO: Extend Frame Delay range to 19 to accommodate PAL land too
|
||||
- WIFI/LAKKA: Add nmcli to wifi drivers
|
||||
- WIFI/LAKKA: Add wifi configuration menu
|
||||
- X11: fix fullscreen when swapping monitors/resolution
|
||||
|
||||
# 1.9.4
|
||||
- CHEEVOS: update rcheevos to v10.0.0
|
||||
- CONTENT LOADING/FILE IO: Prevent unnecessary extraction (to disk) of compressed content files when need_fullpath is false
|
||||
@@ -15,6 +80,7 @@
|
||||
- PS3/PSL1GHT: Joypad driver works again
|
||||
- PSTV: Fix Vita input driver for PSTV
|
||||
- PSTV: Support for 720p on PSTV when using 'Unlock framebuffer' in Sharpscale plugin
|
||||
- PSTV: Fix scaling on PSTV when not using 720p
|
||||
- RPNG: Fix some memory corruption if processing broken input PNG file
|
||||
- SECURITY: Fix CVE-2021-28927
|
||||
|
||||
|
||||
+40
-3
@@ -304,7 +304,6 @@ OBJ += \
|
||||
input/common/input_hid_common.o \
|
||||
led/led_driver.o \
|
||||
gfx/video_coord_array.o \
|
||||
gfx/video_crt_switch.o \
|
||||
gfx/gfx_display.o \
|
||||
gfx/gfx_animation.o \
|
||||
gfx/gfx_thumbnail_path.o \
|
||||
@@ -771,6 +770,11 @@ ifeq ($(HAVE_LAKKA), 1)
|
||||
OBJ += misc/cpufreq/cpufreq.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_WIFI), 1)
|
||||
OBJ += wifi/drivers/nmcli.o
|
||||
DEFINES += -DHAVE_WIFI
|
||||
endif
|
||||
|
||||
# Audio
|
||||
|
||||
ifeq ($(HAVE_COREAUDIO), 1)
|
||||
@@ -2107,8 +2111,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
INCLUDE_DIRS += -Ideps/rcheevos/include
|
||||
|
||||
OBJ += cheevos/cheevos.o \
|
||||
cheevos/badges.o \
|
||||
cheevos/cheevos_memory.o \
|
||||
cheevos/cheevos_menu.o \
|
||||
cheevos/cheevos_parser.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
|
||||
deps/rcheevos/src/rcheevos/alloc.o \
|
||||
@@ -2120,6 +2123,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
deps/rcheevos/src/rcheevos/lboard.o \
|
||||
deps/rcheevos/src/rcheevos/memref.o \
|
||||
deps/rcheevos/src/rcheevos/operand.o \
|
||||
deps/rcheevos/src/rcheevos/rc_libretro.o \
|
||||
deps/rcheevos/src/rcheevos/richpresence.o \
|
||||
deps/rcheevos/src/rcheevos/runtime.o \
|
||||
deps/rcheevos/src/rcheevos/runtime_progress.o \
|
||||
@@ -2269,6 +2273,39 @@ ifeq ($(HAVE_FFMPEG), 1)
|
||||
INCLUDE_DIRS += -Iffmpeg
|
||||
endif
|
||||
|
||||
# CRT mode switching
|
||||
ifeq ($(HAVE_CRTSWITCHRES), 1)
|
||||
INCLUDE_DIRS += -I$(DEPS_DIR)/switchres
|
||||
OBJ += gfx/video_crt_switch.o \
|
||||
$(DEPS_DIR)/switchres/monitor.o \
|
||||
$(DEPS_DIR)/switchres/modeline.o \
|
||||
$(DEPS_DIR)/switchres/switchres.o \
|
||||
$(DEPS_DIR)/switchres/display.o \
|
||||
$(DEPS_DIR)/switchres/custom_video.o \
|
||||
$(DEPS_DIR)/switchres/log.o \
|
||||
$(DEPS_DIR)/switchres/switchres_wrapper.o \
|
||||
$(DEPS_DIR)/switchres/edid.o
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
OBJ += $(DEPS_DIR)/switchres/display_windows.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_ati_family.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_ati.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_adl.o \
|
||||
$(DEPS_DIR)/switchres/custom_video_pstrip.o \
|
||||
$(DEPS_DIR)/switchres/resync_windows.o
|
||||
endif
|
||||
ifneq ($(findstring Linux,$(OS)),)
|
||||
OBJ += $(DEPS_DIR)/switchres/display_linux.o
|
||||
ifeq ($(HAVE_X11)$(HAVE_XRANDR), 11)
|
||||
OBJ += $(DEPS_DIR)/switchres/custom_video_xrandr.o
|
||||
DEFINES += -DSR_WITH_XRANDR
|
||||
endif
|
||||
endif
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
DEFINES += -DSR_WIN32_STATIC
|
||||
endif
|
||||
LIBS += -lstdc++
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_COMPRESSION), 1)
|
||||
DEFINES += -DHAVE_COMPRESSION
|
||||
OBJ += tasks/task_decompress.o
|
||||
|
||||
+1
-1
@@ -490,7 +490,7 @@ else ifeq ($(platform), windows_msvc2005_x86)
|
||||
CXX = cl.exe
|
||||
LD = link.exe
|
||||
|
||||
PLATCFLAGS += -D_WIN32 -D_WIN32_WINNT=0x0410 -D__STDC_CONSTANT_MACROS -D_MBCS -D__SSE__
|
||||
PLATCFLAGS += -D_WIN32 -D_WIN32_WINNT=0x0410 -D__STDC_CONSTANT_MACROS -D_MBCS
|
||||
PLATCFLAGS += -DHAVE_OPENGL -DHAVE_OPENGL1 -DHAVE_GLSL -DHAVE_OZONE -DHAVE_GFX_WIDGETS -DHAVE_CC_RESAMPLER
|
||||
PLATCFLAGS += -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_CDROM
|
||||
PLATCFLAGS += -bigobj
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ ifeq ($(WHOLE_ARCHIVE_LINK), 1)
|
||||
WHOLE_END := -Wl,--no-whole-archive
|
||||
endif
|
||||
|
||||
INCDIR = deps deps/stb deps/7zip deps/pthreads deps/pthreads/platform/psp deps/pthreads/platform/helper libretro-common/include libretro-common/include/compat/zlib
|
||||
INCDIR = deps deps/stb deps/7zip libretro-common/include libretro-common/include/compat/zlib
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math -fsingle-precision-constant
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
@@ -27,7 +27,7 @@ RARCH_DEFINES = -DPSP -D_MIPS_ARCH_ALLEGREX -DHAVE_LANGEXTRA -DHAVE_ZLIB -DHAVE_
|
||||
|
||||
LIBDIR =
|
||||
LDFLAGS =
|
||||
LIBS = $(WHOLE_START) -lretro_psp1 $(WHOLE_END) -lstdc++ -lpspgu -lpspgum -lm -lpspaudio -lpspfpu -lpsppower -lpsprtc
|
||||
LIBS = $(WHOLE_START) -lretro_psp1 $(WHOLE_END) -lstdc++ -lpspgu -lpspgum -lm -lpspaudio -lpspfpu -lpsppower -lpsprtc -lpthread-psp
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
RARCH_DEFINES += -DHAVE_THREADS
|
||||
|
||||
+3
-1
@@ -219,7 +219,9 @@ opk: $(TARGET)
|
||||
echo "$$DESKTOP_ENTRY" > default.gcw0.desktop
|
||||
rm -f $(OPK_NAME)
|
||||
cp media/ico_src/icon32.png retroarch.png
|
||||
if [ $$STRIP_BIN -eq 1 ]; then $(STRIP) --strip-unneeded retroarch; fi
|
||||
ifeq ($(STRIP_BIN),1)
|
||||
$(STRIP) --strip-unneeded retroarch
|
||||
endif
|
||||
$(GCW0_MK_SQUASH_FS) retroarch default.gcw0.desktop retroarch.png $(OPK_NAME) -all-root -no-xattrs -noappend -no-exports
|
||||
rm -f default.gcw0.desktop retroarch.png
|
||||
|
||||
|
||||
@@ -219,7 +219,9 @@ opk: $(TARGET)
|
||||
echo "$$DESKTOP_ENTRY" > default.gcw0.desktop
|
||||
rm -f $(OPK_NAME)
|
||||
cp media/ico_src/icon32.png retroarch.png
|
||||
if [ $$STRIP_BIN -eq 1 ]; then $(STRIP) --strip-unneeded retroarch; fi
|
||||
ifeq ($(STRIP_BIN),1)
|
||||
$(STRIP) --strip-unneeded retroarch
|
||||
endif
|
||||
$(GCW0_MK_SQUASH_FS) retroarch default.gcw0.desktop retroarch.png $(OPK_NAME) -all-root -no-xattrs -noappend -no-exports
|
||||
rm -f default.gcw0.desktop retroarch.png
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ PC_DEVELOPMENT_UDP_PORT =
|
||||
OBJ :=
|
||||
|
||||
SCE_LIBC_SIZE := 4194304
|
||||
DEFINES := -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS
|
||||
DEFINES := -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_STB_VORBIS
|
||||
INCDIRS :=
|
||||
|
||||
ifeq ($(GRIFFIN_BUILD), 1)
|
||||
|
||||
@@ -546,7 +546,8 @@ static ssize_t dsound_write(void *data, const void *buf_, size_t size)
|
||||
break;
|
||||
|
||||
if (avail == 0)
|
||||
WaitForSingleObject(ds->event, INFINITE);
|
||||
if (!(WaitForSingleObject(ds->event, 50) == WAIT_OBJECT_0))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ static void *libnx_audren_thread_audio_init(const char *device, unsigned rate, u
|
||||
int mpid;
|
||||
size_t mempool_size;
|
||||
unsigned real_latency;
|
||||
uint32_t thread_priority;
|
||||
int32_t thread_priority;
|
||||
|
||||
RARCH_LOG("[Audio]: Using libnx_audren_thread driver\n");
|
||||
|
||||
|
||||
@@ -371,7 +371,8 @@ static ssize_t xa_write(void *data, const void *buf, size_t size)
|
||||
XAUDIO2_BUFFER xa2buffer;
|
||||
|
||||
while (handle->buffers == MAX_BUFFERS - 1)
|
||||
WaitForSingleObject(handle->hEvent, INFINITE);
|
||||
if (!(WaitForSingleObject(handle->hEvent, 50) == WAIT_OBJECT_0))
|
||||
return -1;
|
||||
|
||||
xa2buffer.Flags = 0;
|
||||
xa2buffer.AudioBytes = handle->bufsize;
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2016 - Andre Leiradella
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <file/file_path.h>
|
||||
|
||||
#include "../file_path_special.h"
|
||||
#include "../configuration.h"
|
||||
#include "../gfx/gfx_display.h"
|
||||
|
||||
#include "badges.h"
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#define CHEEVOS_MENU_BADGE_LIMIT 256
|
||||
/* TODO/FIXME - public global variables */
|
||||
static uintptr_t cheevos_badge_menu_texture_list[CHEEVOS_MENU_BADGE_LIMIT] = { 0 };
|
||||
|
||||
void cheevos_reset_menu_badges(void)
|
||||
{
|
||||
int index;
|
||||
for (index = 0; index < CHEEVOS_MENU_BADGE_LIMIT; ++index)
|
||||
{
|
||||
if (cheevos_badge_menu_texture_list[index])
|
||||
video_driver_texture_unload(&cheevos_badge_menu_texture_list[index]);
|
||||
}
|
||||
|
||||
memset(&cheevos_badge_menu_texture_list, 0,
|
||||
sizeof(cheevos_badge_menu_texture_list));
|
||||
}
|
||||
|
||||
void cheevos_set_menu_badge(int index, const char *badge, bool locked)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (index >= CHEEVOS_MENU_BADGE_LIMIT)
|
||||
return;
|
||||
|
||||
if (!settings || !settings->bools.cheevos_badges_enable)
|
||||
cheevos_badge_menu_texture_list[index] = 0;
|
||||
else
|
||||
cheevos_badge_menu_texture_list[index] =
|
||||
cheevos_get_badge_texture(badge, locked);
|
||||
}
|
||||
|
||||
uintptr_t cheevos_get_menu_badge_texture(int index)
|
||||
{
|
||||
if (index < CHEEVOS_MENU_BADGE_LIMIT)
|
||||
return cheevos_badge_menu_texture_list[index];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uintptr_t cheevos_get_badge_texture(const char *badge, bool locked)
|
||||
{
|
||||
char badge_file[24];
|
||||
char fullpath[PATH_MAX_LENGTH];
|
||||
uintptr_t tex = 0;
|
||||
|
||||
if (!badge)
|
||||
return 0;
|
||||
|
||||
fullpath[0] = badge_file[0] = '\0';
|
||||
|
||||
strlcpy(badge_file, badge, sizeof(badge_file));
|
||||
if (locked)
|
||||
strlcat(badge_file, "_lock", sizeof(badge_file));
|
||||
strlcat(badge_file, FILE_PATH_PNG_EXTENSION, sizeof(badge_file));
|
||||
|
||||
fill_pathname_application_special(fullpath, sizeof(fullpath),
|
||||
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
||||
|
||||
if (!gfx_display_reset_textures_list(badge_file, fullpath,
|
||||
&tex, TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
|
||||
tex = 0;
|
||||
|
||||
return tex;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_BADGE_H
|
||||
#define __RARCH_CHEEVOS_BADGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <boolean.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
void cheevos_reset_menu_badges(void);
|
||||
void cheevos_set_menu_badge(int index, const char *badge, bool locked);
|
||||
uintptr_t cheevos_get_menu_badge_texture(int index);
|
||||
#endif
|
||||
|
||||
uintptr_t cheevos_get_badge_texture(const char* badge, bool locked);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
+198
-599
File diff suppressed because it is too large.
Load diff
+7
-32
@@ -21,64 +21,39 @@
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
#include "../verbosity.h"
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct rcheevos_ctx_desc
|
||||
{
|
||||
unsigned idx;
|
||||
char *s;
|
||||
size_t len;
|
||||
} rcheevos_ctx_desc_t;
|
||||
|
||||
enum
|
||||
{
|
||||
RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
|
||||
RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
|
||||
RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
|
||||
};
|
||||
|
||||
bool rcheevos_load(const void *data);
|
||||
size_t rcheevos_get_serialize_size(void);
|
||||
bool rcheevos_get_serialized_data(void* buffer);
|
||||
bool rcheevos_set_serialized_data(void* buffer);
|
||||
|
||||
void rcheevos_reset_game(bool widgets_ready);
|
||||
|
||||
void rcheevos_populate_menu(void* data);
|
||||
void rcheevos_populate_hardcore_pause_menu(void* data);
|
||||
void rcheevos_get_achievement_state(unsigned index, char* buffer, size_t buffer_size);
|
||||
|
||||
bool rcheevos_get_description(rcheevos_ctx_desc_t *desc);
|
||||
|
||||
void rcheevos_pause_hardcore(void);
|
||||
|
||||
bool rcheevos_unload(void);
|
||||
|
||||
void rcheevos_test(void);
|
||||
|
||||
void rcheevos_reset_game(bool widgets_ready);
|
||||
|
||||
void rcheevos_pause_hardcore(void);
|
||||
void rcheevos_hardcore_enabled_changed(void);
|
||||
void rcheevos_toggle_hardcore_paused(void);
|
||||
bool rcheevos_hardcore_active(void);
|
||||
|
||||
void rcheevos_validate_config_settings(void);
|
||||
|
||||
void rcheevos_leaderboards_enabled_changed(void);
|
||||
|
||||
void rcheevos_test(void);
|
||||
|
||||
void rcheevos_set_support_cheevos(bool state);
|
||||
|
||||
bool rcheevos_get_support_cheevos(void);
|
||||
|
||||
const char* rcheevos_get_hash(void);
|
||||
|
||||
int rcheevos_get_richpresence(char buffer[], int buffer_size);
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked);
|
||||
|
||||
uint8_t* rcheevos_patch_address(unsigned address);
|
||||
|
||||
bool rcheevos_hardcore_active(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_CHEEVOS_H */
|
||||
@@ -0,0 +1,158 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_LOCALS_H
|
||||
#define __RARCH_CHEEVOS_LOCALS_H
|
||||
|
||||
#include "../deps/rcheevos/include/rc_runtime.h"
|
||||
|
||||
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
|
||||
|
||||
#include <../command.h>
|
||||
#include <../verbosity.h>
|
||||
#include <boolean.h>
|
||||
#include <queues/task_queue.h>
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#include <rthreads/rthreads.h>
|
||||
#endif
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/************************************************************************
|
||||
* Logging *
|
||||
************************************************************************/
|
||||
|
||||
/* Define this macro to get extra-verbose log for cheevos. */
|
||||
#define CHEEVOS_VERBOSE
|
||||
|
||||
#define RCHEEVOS_TAG "[RCHEEVOS]: "
|
||||
#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q) free(q); } while (0)
|
||||
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
#define CHEEVOS_LOG RARCH_LOG
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
#else
|
||||
void rcheevos_log(const char *fmt, ...);
|
||||
#define CHEEVOS_LOG rcheevos_log
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
#endif
|
||||
|
||||
/************************************************************************
|
||||
* State *
|
||||
************************************************************************/
|
||||
|
||||
enum
|
||||
{
|
||||
RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
|
||||
RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
|
||||
RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
|
||||
};
|
||||
|
||||
typedef struct rcheevos_racheevo_t
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* badge;
|
||||
const char* memaddr;
|
||||
unsigned id;
|
||||
unsigned points;
|
||||
|
||||
retro_time_t unlock_time;
|
||||
uint8_t active;
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
uint8_t menu_bucket;
|
||||
uint8_t menu_progress;
|
||||
uint8_t menu_badge_grayscale;
|
||||
uintptr_t menu_badge_texture;
|
||||
#endif
|
||||
|
||||
} rcheevos_racheevo_t;
|
||||
|
||||
typedef struct rcheevos_ralboard_t
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* mem;
|
||||
unsigned id;
|
||||
unsigned format;
|
||||
} rcheevos_ralboard_t;
|
||||
|
||||
typedef struct rcheevos_rapatchdata_t
|
||||
{
|
||||
char* title;
|
||||
rcheevos_racheevo_t* core;
|
||||
rcheevos_racheevo_t* unofficial;
|
||||
rcheevos_ralboard_t* lboards;
|
||||
char* richpresence_script;
|
||||
|
||||
unsigned game_id;
|
||||
unsigned console_id;
|
||||
unsigned core_count;
|
||||
unsigned unofficial_count;
|
||||
unsigned lboard_count;
|
||||
} rcheevos_rapatchdata_t;
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
typedef struct rcheevos_menuitem_t
|
||||
{
|
||||
rcheevos_racheevo_t* cheevo;
|
||||
enum msg_hash_enums state_label_idx;
|
||||
} rcheevos_menuitem_t;
|
||||
|
||||
void rcheevos_menu_reset_badges(void);
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct rcheevos_locals_t
|
||||
{
|
||||
rc_runtime_t runtime; /* rcheevos runtime state */
|
||||
rcheevos_rapatchdata_t patchdata; /* achievement/leaderboard data from the server */
|
||||
rc_libretro_memory_regions_t memory;/* achievement addresses to core memory mappings */
|
||||
|
||||
retro_task_t* task; /* load task */
|
||||
#ifdef HAVE_THREADS
|
||||
slock_t* task_lock; /* mutex for starting/stopping load task */
|
||||
enum event_command queued_command; /* action queued by background thread to be run on main thread */
|
||||
#endif
|
||||
|
||||
char token[32]; /* user's session token */
|
||||
char hash[33]; /* retroachievements hash for current content */
|
||||
char user_agent_prefix[128]; /* RetroArch/OS version information */
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
rcheevos_menuitem_t* menuitems; /* array of items for the achievements quick menu */
|
||||
unsigned menuitem_capacity; /* maximum number of items in the menuitems array */
|
||||
unsigned menuitem_count; /* current number of items in the menuitems array */
|
||||
#endif
|
||||
|
||||
bool hardcore_active; /* hardcore functionality is active */
|
||||
bool loaded; /* load task has completed */
|
||||
bool core_supports; /* false if core explicitly disables achievements */
|
||||
bool leaderboards_enabled; /* leaderboards are enabled */
|
||||
bool leaderboard_notifications; /* leaderboard notifications are enabled */
|
||||
bool leaderboard_trackers; /* leaderboard trackers are enabled */
|
||||
} rcheevos_locals_t;
|
||||
|
||||
rcheevos_locals_t* get_rcheevos_locals(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_LOCALS_H */
|
||||
@@ -1,352 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2016 - Andre Leiradella
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cheevos_memory.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "../retroarch.h"
|
||||
#include "../verbosity.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rcheevos.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
uint8_t* rcheevos_memory_find(
|
||||
const rcheevos_memory_regions_t* regions, unsigned address)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < regions->count; ++i)
|
||||
{
|
||||
const size_t size = regions->size[i];
|
||||
if (address < size)
|
||||
{
|
||||
if (regions->data[i] == NULL)
|
||||
break;
|
||||
|
||||
return ®ions->data[i][address];
|
||||
}
|
||||
|
||||
address -= size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* rcheevos_memory_type(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case RC_MEMORY_TYPE_SAVE_RAM:
|
||||
return "SRAM";
|
||||
case RC_MEMORY_TYPE_VIDEO_RAM:
|
||||
return "VRAM";
|
||||
case RC_MEMORY_TYPE_UNUSED:
|
||||
return "UNUSED";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "SYSTEM RAM";
|
||||
}
|
||||
|
||||
static void rcheevos_memory_register_region(rcheevos_memory_regions_t* regions,
|
||||
int type, uint8_t* data, size_t size, const char* description)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
if (regions->count == MAX_MEMORY_REGIONS)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Too many memory memory regions to register\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data && regions->count > 0 && !regions->data[regions->count - 1])
|
||||
{
|
||||
/* extend null region */
|
||||
regions->size[regions->count - 1] += size;
|
||||
}
|
||||
else if (data && regions->count > 0 &&
|
||||
data == (regions->data[regions->count - 1] + regions->size[regions->count - 1]))
|
||||
{
|
||||
/* extend non-null region */
|
||||
regions->size[regions->count - 1] += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* create new region */
|
||||
regions->data[regions->count] = data;
|
||||
regions->size[regions->count] = size;
|
||||
++regions->count;
|
||||
}
|
||||
|
||||
regions->total_size += size;
|
||||
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Registered 0x%04X bytes of %s at $%06X (%s)\n", (unsigned)size,
|
||||
rcheevos_memory_type(type), (unsigned)(regions->total_size - size), description);
|
||||
}
|
||||
|
||||
static void rcheevos_memory_init_without_regions(
|
||||
rcheevos_memory_regions_t* regions)
|
||||
{
|
||||
/* no regions specified, assume system RAM followed by save RAM */
|
||||
char description[64];
|
||||
retro_ctx_memory_info_t meminfo;
|
||||
|
||||
snprintf(description, sizeof(description), "offset 0x%06x", 0);
|
||||
|
||||
meminfo.id = RETRO_MEMORY_SYSTEM_RAM;
|
||||
core_get_memory(&meminfo);
|
||||
rcheevos_memory_register_region(regions, RC_MEMORY_TYPE_SYSTEM_RAM, (uint8_t*)meminfo.data, meminfo.size, description);
|
||||
|
||||
meminfo.id = RETRO_MEMORY_SAVE_RAM;
|
||||
core_get_memory(&meminfo);
|
||||
rcheevos_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, (uint8_t*)meminfo.data, meminfo.size, description);
|
||||
}
|
||||
|
||||
static const rarch_memory_descriptor_t* rcheevos_memory_get_descriptor(const rarch_memory_map_t* mmap, unsigned real_address)
|
||||
{
|
||||
const rarch_memory_descriptor_t* desc = mmap->descriptors;
|
||||
const rarch_memory_descriptor_t* end = desc + mmap->num_descriptors;
|
||||
|
||||
if (mmap->num_descriptors == 0)
|
||||
return NULL;
|
||||
|
||||
for (; desc < end; desc++)
|
||||
{
|
||||
if (desc->core.select == 0)
|
||||
{
|
||||
/* if select is 0, attempt to explcitly match the address */
|
||||
if (real_address >= desc->core.start && real_address < desc->core.start + desc->core.len)
|
||||
return desc;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise, attempt to match the address by matching the select bits */
|
||||
if (((desc->core.start ^ real_address) & desc->core.select) == 0)
|
||||
{
|
||||
/* sanity check - make sure the descriptor is large enough to hold the target address */
|
||||
if (real_address - desc->core.start < desc->core.len)
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rcheevos_memory_init_from_memory_map(rcheevos_memory_regions_t* regions, const rarch_memory_map_t* mmap, const rc_memory_regions_t* console_regions)
|
||||
{
|
||||
char description[64];
|
||||
unsigned i;
|
||||
uint8_t* region_start;
|
||||
uint8_t* desc_start;
|
||||
size_t desc_size;
|
||||
size_t offset;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i)
|
||||
{
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
size_t console_region_size = console_region->end_address - console_region->start_address + 1;
|
||||
unsigned real_address = console_region->real_address;
|
||||
|
||||
while (console_region_size > 0)
|
||||
{
|
||||
const rarch_memory_descriptor_t* desc = rcheevos_memory_get_descriptor(mmap, real_address);
|
||||
if (!desc)
|
||||
{
|
||||
if (console_region->type != RC_MEMORY_TYPE_UNUSED)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n",
|
||||
real_address - console_region->real_address + console_region->start_address);
|
||||
}
|
||||
|
||||
rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
break;
|
||||
}
|
||||
|
||||
offset = real_address - desc->core.start;
|
||||
snprintf(description, sizeof(description),
|
||||
"descriptor %u, offset 0x%06X",
|
||||
(int)(desc - mmap->descriptors) + 1, (int)offset);
|
||||
|
||||
if (desc->core.ptr)
|
||||
{
|
||||
desc_start = (uint8_t*)desc->core.ptr + desc->core.offset;
|
||||
region_start = desc_start + offset;
|
||||
}
|
||||
else
|
||||
region_start = NULL;
|
||||
|
||||
desc_size = desc->core.len - offset;
|
||||
|
||||
if (console_region_size > desc_size)
|
||||
{
|
||||
if (desc_size == 0)
|
||||
{
|
||||
if (console_region->type != RC_MEMORY_TYPE_UNUSED)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n",
|
||||
real_address - console_region->real_address + console_region->start_address);
|
||||
}
|
||||
|
||||
rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
|
||||
console_region_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_memory_register_region(regions, console_region->type, region_start, desc_size, description);
|
||||
console_region_size -= desc_size;
|
||||
real_address += desc_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_memory_register_region(regions, console_region->type, region_start, console_region_size, description);
|
||||
console_region_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned rcheevos_memory_console_region_to_ram_type(int region_type)
|
||||
{
|
||||
switch (region_type)
|
||||
{
|
||||
case RC_MEMORY_TYPE_SAVE_RAM:
|
||||
return RETRO_MEMORY_SAVE_RAM;
|
||||
case RC_MEMORY_TYPE_VIDEO_RAM:
|
||||
return RETRO_MEMORY_VIDEO_RAM;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return RETRO_MEMORY_SYSTEM_RAM;
|
||||
}
|
||||
|
||||
static void rcheevos_memory_init_from_unmapped_memory(rcheevos_memory_regions_t* regions, const rc_memory_regions_t* console_regions, int console)
|
||||
{
|
||||
char description[64];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i)
|
||||
{
|
||||
size_t offset;
|
||||
unsigned j;
|
||||
retro_ctx_memory_info_t meminfo;
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
const size_t console_region_size =
|
||||
console_region->end_address - console_region->start_address + 1;
|
||||
unsigned base_address = 0;
|
||||
|
||||
meminfo.id = rcheevos_memory_console_region_to_ram_type(console_region->type);
|
||||
|
||||
for (j = 0; j <= i; ++j)
|
||||
{
|
||||
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
||||
if (rcheevos_memory_console_region_to_ram_type(
|
||||
console_region2->type) == meminfo.id)
|
||||
{
|
||||
base_address = console_region2->start_address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset = console_region->start_address - base_address;
|
||||
|
||||
core_get_memory(&meminfo);
|
||||
|
||||
if (offset < meminfo.size)
|
||||
{
|
||||
meminfo.size -= offset;
|
||||
|
||||
if (meminfo.data)
|
||||
{
|
||||
snprintf(description, sizeof(description),
|
||||
"offset 0x%06X", (int)offset);
|
||||
meminfo.data = (uint8_t*)meminfo.data + offset;
|
||||
}
|
||||
else
|
||||
snprintf(description, sizeof(description), "null filler");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (console_region->type != RC_MEMORY_TYPE_UNUSED)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Could not map region starting at $%06X\n", console_region->start_address);
|
||||
}
|
||||
|
||||
meminfo.data = NULL;
|
||||
meminfo.size = 0;
|
||||
}
|
||||
|
||||
if (console_region_size > meminfo.size)
|
||||
{
|
||||
/* want more than what is available, take what we can and null fill the rest */
|
||||
rcheevos_memory_register_region(regions, console_region->type, (uint8_t*)meminfo.data, meminfo.size, description);
|
||||
rcheevos_memory_register_region(regions, console_region->type, NULL, console_region_size - meminfo.size, "null filler");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* only take as much as we need */
|
||||
rcheevos_memory_register_region(regions, console_region->type, (uint8_t*)meminfo.data, console_region_size, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_memory_destroy(rcheevos_memory_regions_t* regions)
|
||||
{
|
||||
memset(regions, 0, sizeof(*regions));
|
||||
}
|
||||
|
||||
bool rcheevos_memory_init(rcheevos_memory_regions_t* regions, int console)
|
||||
{
|
||||
unsigned i;
|
||||
const rc_memory_regions_t* console_regions = rc_console_memory_regions(console);
|
||||
rcheevos_memory_regions_t new_regions;
|
||||
bool has_valid_region = false;
|
||||
|
||||
if (!regions)
|
||||
return false;
|
||||
|
||||
memset(&new_regions, 0, sizeof(new_regions));
|
||||
|
||||
if (console_regions == NULL || console_regions->num_regions == 0)
|
||||
{
|
||||
rcheevos_memory_init_without_regions(&new_regions);
|
||||
}
|
||||
else
|
||||
{
|
||||
rarch_system_info_t* system = runloop_get_system_info();
|
||||
if (system->mmaps.num_descriptors != 0)
|
||||
rcheevos_memory_init_from_memory_map(&new_regions, &system->mmaps, console_regions);
|
||||
else
|
||||
rcheevos_memory_init_from_unmapped_memory(&new_regions, console_regions, console);
|
||||
}
|
||||
|
||||
/* determine if any valid regions were found */
|
||||
for (i = 0; i < new_regions.count; i++)
|
||||
{
|
||||
if (new_regions.data[i])
|
||||
{
|
||||
has_valid_region = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(regions, &new_regions, sizeof(*regions));
|
||||
return has_valid_region;
|
||||
}
|
||||
@@ -0,0 +1,709 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cheevos_locals.h"
|
||||
|
||||
#include "../gfx/gfx_display.h"
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#include "cheevos.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rc_runtime_types.h"
|
||||
|
||||
#include "../file_path_special.h"
|
||||
#include "../menu/menu_driver.h"
|
||||
#include "../menu/menu_entries.h"
|
||||
|
||||
#include <features/features_cpu.h>
|
||||
|
||||
enum rcheevos_menuitem_bucket
|
||||
{
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNKNOWN = 0,
|
||||
RCHEEVOS_MENUITEM_BUCKET_LOCKED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNLOCKED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE
|
||||
};
|
||||
|
||||
static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
cheevo->menu_progress = 0;
|
||||
|
||||
if (!cheevo->memaddr)
|
||||
{
|
||||
/* non-active unsupported achievement */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED;
|
||||
}
|
||||
else if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE))
|
||||
{
|
||||
/* non-active unlocked in hardcore achievement */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNLOCKED;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rc_trigger_t* trigger;
|
||||
|
||||
if (!rcheevos_locals->hardcore_active && !(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
|
||||
{
|
||||
/* non-active unlocked in softcore achievement in softcore mode */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNLOCKED;
|
||||
return;
|
||||
}
|
||||
|
||||
/* active achievement */
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_LOCKED;
|
||||
|
||||
trigger = rc_runtime_get_achievement(&rcheevos_locals->runtime, cheevo->id);
|
||||
if (trigger)
|
||||
{
|
||||
if (trigger->measured_value && trigger->measured_target)
|
||||
{
|
||||
const unsigned long clamped_value = (unsigned long)
|
||||
MIN(trigger->measured_value, trigger->measured_target);
|
||||
cheevo->menu_progress =
|
||||
(uint8_t)((clamped_value * 100) / trigger->measured_target);
|
||||
}
|
||||
|
||||
if (trigger->state == RC_TRIGGER_STATE_PRIMED)
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE;
|
||||
else if (cheevo->menu_progress >= 80)
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rcheevos_menu_update_buckets(bool cheevos_test_unofficial)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
|
||||
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
rcheevos_menu_update_bucket(cheevo);
|
||||
++cheevo;
|
||||
}
|
||||
|
||||
if (cheevos_test_unofficial)
|
||||
{
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
rcheevos_menu_update_bucket(cheevo);
|
||||
++cheevo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
const rcheevos_menuitem_t* menuitem = &rcheevos_locals->menuitems[menu_offset];
|
||||
const rcheevos_racheevo_t* cheevo = menuitem->cheevo;
|
||||
if (cheevo)
|
||||
{
|
||||
if (cheevo->menu_progress)
|
||||
{
|
||||
snprintf(buffer, len, "%s - %d%%",
|
||||
msg_hash_to_str(menuitem->state_label_idx),
|
||||
cheevo->menu_progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), len);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
buffer[0] = '\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t len)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
const rcheevos_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
|
||||
if (cheevo && buffer)
|
||||
{
|
||||
strlcpy(buffer, cheevo->description, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
buffer[0] = '\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void rcheevos_menu_reset_badges(void)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
|
||||
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
++cheevo;
|
||||
}
|
||||
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
|
||||
while (cheevo < stop)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
++cheevo;
|
||||
}
|
||||
}
|
||||
|
||||
static rcheevos_menuitem_t* rcheevos_menu_allocate(
|
||||
rcheevos_locals_t* rcheevos_locals, rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem;
|
||||
|
||||
if (rcheevos_locals->menuitem_count == rcheevos_locals->menuitem_capacity)
|
||||
{
|
||||
if (rcheevos_locals->menuitems)
|
||||
{
|
||||
rcheevos_menuitem_t* new_menuitems;
|
||||
rcheevos_locals->menuitem_capacity += 32;
|
||||
new_menuitems = (rcheevos_menuitem_t*)realloc(rcheevos_locals->menuitems,
|
||||
rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
|
||||
if (new_menuitems)
|
||||
{
|
||||
rcheevos_locals->menuitems = new_menuitems;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* realloc failed */
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG " could not allocate space for %u menu items\n",
|
||||
rcheevos_locals->menuitem_capacity);
|
||||
rcheevos_locals->menuitem_capacity -= 32;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_locals->menuitem_capacity = 64;
|
||||
rcheevos_locals->menuitems = (rcheevos_menuitem_t*)
|
||||
malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
|
||||
if (!rcheevos_locals->menuitems)
|
||||
{
|
||||
/* malloc failed */
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG " could not allocate space for %u menu items\n",
|
||||
rcheevos_locals->menuitem_capacity);
|
||||
rcheevos_locals->menuitem_capacity = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menuitem = &rcheevos_locals->menuitems[rcheevos_locals->menuitem_count++];
|
||||
menuitem->cheevo = cheevo;
|
||||
menuitem->state_label_idx = MSG_UNKNOWN;
|
||||
return menuitem;
|
||||
}
|
||||
|
||||
static void rcheevos_menu_append_header(rcheevos_locals_t* rcheevos_locals,
|
||||
enum msg_hash_enums label)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals, NULL);
|
||||
if (menuitem)
|
||||
menuitem->state_label_idx = label;
|
||||
}
|
||||
|
||||
static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
bool badge_grayscale = false;
|
||||
switch (cheevo->menu_bucket)
|
||||
{
|
||||
case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
|
||||
badge_grayscale = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
badge_grayscale = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cheevo->menu_badge_texture || cheevo->menu_badge_grayscale != badge_grayscale)
|
||||
{
|
||||
uintptr_t new_badge_texture =
|
||||
rcheevos_get_badge_texture(cheevo->badge, badge_grayscale);
|
||||
|
||||
if (new_badge_texture)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
|
||||
cheevo->menu_badge_texture = new_badge_texture;
|
||||
cheevo->menu_badge_grayscale = badge_grayscale;
|
||||
}
|
||||
/* menu_badge_grayscale is overloaded such that any value greater than 1 indicates
|
||||
* the server default image is being used */
|
||||
else if (cheevo->menu_badge_grayscale < 2)
|
||||
{
|
||||
if (cheevo->menu_badge_texture)
|
||||
video_driver_texture_unload(&cheevo->menu_badge_texture);
|
||||
|
||||
/* requested badge is not available, check for server default */
|
||||
cheevo->menu_badge_texture =
|
||||
rcheevos_get_badge_texture("00000", false);
|
||||
|
||||
if (cheevo->menu_badge_texture)
|
||||
cheevo->menu_badge_grayscale = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rcheevos_menu_append_items(rcheevos_locals_t* rcheevos_locals,
|
||||
bool cheevos_test_unofficial, enum rcheevos_menuitem_bucket bucket)
|
||||
{
|
||||
const settings_t *settings = config_get_ptr();
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
|
||||
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
bool processing_unofficial = false;
|
||||
const unsigned first_index = rcheevos_locals->menuitem_count;
|
||||
|
||||
do
|
||||
{
|
||||
if (cheevo == stop)
|
||||
{
|
||||
if (!cheevos_test_unofficial || processing_unofficial)
|
||||
break;
|
||||
|
||||
processing_unofficial = true;
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cheevo->menu_bucket == bucket)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals, cheevo);
|
||||
if (!menuitem)
|
||||
return;
|
||||
|
||||
switch (cheevo->menu_bucket)
|
||||
{
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED:
|
||||
{
|
||||
/* insert the item such that the unlock times are descending */
|
||||
unsigned entry_index = rcheevos_locals->menuitem_count - 1;
|
||||
while (entry_index > first_index)
|
||||
{
|
||||
rcheevos_menuitem_t* prev_menuitem = menuitem - 1;
|
||||
if (prev_menuitem->cheevo->unlock_time >= cheevo->unlock_time)
|
||||
break;
|
||||
|
||||
memcpy(menuitem, prev_menuitem, sizeof(rcheevos_menuitem_t));
|
||||
menuitem = prev_menuitem;
|
||||
--entry_index;
|
||||
}
|
||||
|
||||
menuitem->cheevo = cheevo;
|
||||
}
|
||||
/* fallthrough to RCHEEVOS_MENUITEM_BUCKET_UNLOCKED */
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNLOCKED:
|
||||
if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE))
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY_HARDCORE;
|
||||
else
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
|
||||
{
|
||||
/* insert the item such that the progresses are descending */
|
||||
unsigned entry_index = rcheevos_locals->menuitem_count - 1;
|
||||
while (entry_index > first_index)
|
||||
{
|
||||
rcheevos_menuitem_t* prev_menuitem = menuitem - 1;
|
||||
if (prev_menuitem->cheevo->menu_progress >= cheevo->menu_progress)
|
||||
break;
|
||||
|
||||
memcpy(menuitem, prev_menuitem, sizeof(rcheevos_menuitem_t));
|
||||
menuitem = prev_menuitem;
|
||||
--entry_index;
|
||||
}
|
||||
|
||||
menuitem->cheevo = cheevo;
|
||||
}
|
||||
/* fallthrough to default */
|
||||
|
||||
default:
|
||||
if (processing_unofficial)
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY;
|
||||
else if (!(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
|
||||
else
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cheevo->badge && cheevo->badge[0] && settings &&
|
||||
settings->bools.cheevos_badges_enable)
|
||||
{
|
||||
rcheevos_menu_update_badge(cheevo);
|
||||
}
|
||||
}
|
||||
|
||||
++cheevo;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
rcheevos_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
|
||||
if (cheevo)
|
||||
{
|
||||
/* if we're using the placeholder badge, check to see if the real badge
|
||||
* has become available (do this roughly once a second) */
|
||||
if (cheevo->menu_badge_grayscale >= 2)
|
||||
{
|
||||
if (++cheevo->menu_badge_grayscale == 64)
|
||||
{
|
||||
cheevo->menu_badge_grayscale = 2;
|
||||
rcheevos_menu_update_badge(cheevo);
|
||||
}
|
||||
}
|
||||
|
||||
return cheevo->menu_badge_texture;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rcheevos_menu_populate_hardcore_pause_submenu(void* data)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
|
||||
const settings_t* settings = config_get_ptr();
|
||||
const bool cheevos_hardcore_mode_enable = settings->bools.cheevos_hardcore_mode_enable;
|
||||
|
||||
if (cheevos_hardcore_mode_enable && rcheevos_locals->loaded)
|
||||
{
|
||||
if (rcheevos_locals->hardcore_active)
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0);
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0);
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_RESUME),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_menu_populate(void* data)
|
||||
{
|
||||
menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
|
||||
rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
const settings_t* settings = config_get_ptr();
|
||||
const bool cheevos_test_unofficial = settings->bools.cheevos_test_unofficial;
|
||||
unsigned num_locked = 0;
|
||||
unsigned num_unlocked = 0;
|
||||
unsigned num_recently_unlocked = 0;
|
||||
unsigned num_unsupported = 0;
|
||||
unsigned num_active_challenges = 0;
|
||||
unsigned num_almost_there = 0;
|
||||
|
||||
if (rcheevos_locals->loaded)
|
||||
{
|
||||
const retro_time_t now = cpu_features_get_time_usec();
|
||||
const retro_time_t recent_unlock_time = now - (10 * 60 * 1000000); /* 10 minutes ago */
|
||||
bool processing_unofficial = false;
|
||||
rcheevos_racheevo_t* cheevo = NULL;
|
||||
rcheevos_racheevo_t* stop = NULL;
|
||||
|
||||
/* first menu item is the Pause/Resume Hardcore option (unless hardcore is disabled) */
|
||||
if (settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable)
|
||||
{
|
||||
if (rcheevos_locals->hardcore_active)
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
|
||||
else
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
|
||||
}
|
||||
|
||||
/* update the bucket for each achievement */
|
||||
rcheevos_menu_update_buckets(cheevos_test_unofficial);
|
||||
|
||||
/* count items in each bucket */
|
||||
cheevo = rcheevos_locals->patchdata.core;
|
||||
stop = cheevo + rcheevos_locals->patchdata.core_count;
|
||||
|
||||
do
|
||||
{
|
||||
if (cheevo == stop)
|
||||
{
|
||||
if (!cheevos_test_unofficial || processing_unofficial)
|
||||
break;
|
||||
|
||||
processing_unofficial = true;
|
||||
cheevo = rcheevos_locals->patchdata.unofficial;
|
||||
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cheevo->menu_bucket)
|
||||
{
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNLOCKED:
|
||||
if (cheevo->unlock_time && cheevo->unlock_time >= recent_unlock_time)
|
||||
{
|
||||
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED;
|
||||
++num_recently_unlocked;
|
||||
}
|
||||
else
|
||||
{
|
||||
++num_unlocked;
|
||||
}
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
|
||||
++num_locked;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
|
||||
++num_unsupported;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
|
||||
++num_active_challenges;
|
||||
break;
|
||||
|
||||
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
|
||||
++num_almost_there;
|
||||
break;
|
||||
}
|
||||
|
||||
++cheevo;
|
||||
} while(true);
|
||||
|
||||
if (!rcheevos_locals->menuitems)
|
||||
{
|
||||
/* reserve space for all achievements and up to 6 headers before we need to realloc */
|
||||
rcheevos_locals->menuitem_capacity = rcheevos_locals->patchdata.core_count + 6;
|
||||
if (cheevos_test_unofficial)
|
||||
rcheevos_locals->menuitem_capacity += rcheevos_locals->patchdata.unofficial_count;
|
||||
|
||||
rcheevos_locals->menuitems = (rcheevos_menuitem_t*)
|
||||
malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
if (!rcheevos_locals->menuitems)
|
||||
rcheevos_locals->menuitem_capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset menu */
|
||||
rcheevos_locals->menuitem_count = 0;
|
||||
|
||||
/* active challenges */
|
||||
if (num_active_challenges)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY);
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE);
|
||||
}
|
||||
|
||||
/* recently unlocked */
|
||||
if (num_recently_unlocked)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY);
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED);
|
||||
}
|
||||
|
||||
/* almost there */
|
||||
if (num_almost_there)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY);
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE);
|
||||
}
|
||||
|
||||
/* locked */
|
||||
if (num_locked)
|
||||
{
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY);
|
||||
}
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_LOCKED);
|
||||
}
|
||||
|
||||
/* unlocked */
|
||||
if (num_unlocked)
|
||||
{
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
rcheevos_menu_append_header(rcheevos_locals,
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY);
|
||||
}
|
||||
|
||||
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNLOCKED);
|
||||
}
|
||||
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
/* convert to menu entries */
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_locals->menuitems;
|
||||
rcheevos_menuitem_t* stop = menuitem + rcheevos_locals->menuitem_count;
|
||||
char buffer[128];
|
||||
unsigned idx = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (menuitem->cheevo)
|
||||
{
|
||||
menu_entries_append_enum(info->list, menuitem->cheevo->title,
|
||||
menuitem->cheevo->description,
|
||||
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
|
||||
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "----- %s -----",
|
||||
msg_hash_to_str(menuitem->state_label_idx));
|
||||
|
||||
menu_entries_append_enum(info->list, buffer, "",
|
||||
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
|
||||
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0);
|
||||
}
|
||||
|
||||
++idx;
|
||||
++menuitem;
|
||||
} while (menuitem != stop);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no achievements found */
|
||||
if (!rcheevos_locals->core_supports)
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE),
|
||||
MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
else if (!rcheevos_locals->token[0])
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_LOGGED_IN),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_NOT_LOGGED_IN),
|
||||
MENU_ENUM_LABEL_NOT_LOGGED_IN,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu_entries_append_enum(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ACHIEVEMENTS_TO_DISPLAY),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_NO_ACHIEVEMENTS_TO_DISPLAY),
|
||||
MENU_ENUM_LABEL_NO_ACHIEVEMENTS_TO_DISPLAY,
|
||||
FILE_TYPE_NONE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_MENU */
|
||||
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked)
|
||||
{
|
||||
char badge_file[24];
|
||||
char fullpath[PATH_MAX_LENGTH];
|
||||
uintptr_t tex = 0;
|
||||
|
||||
if (!badge)
|
||||
return 0;
|
||||
|
||||
snprintf(badge_file, sizeof(badge_file), "%s%s%s", badge,
|
||||
locked ? "_lock" : "", FILE_PATH_PNG_EXTENSION);
|
||||
|
||||
fill_pathname_application_special(fullpath, sizeof(fullpath),
|
||||
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
||||
|
||||
if (!gfx_display_reset_textures_list(badge_file, fullpath,
|
||||
&tex, TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
|
||||
{
|
||||
tex = 0;
|
||||
}
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
@@ -12,33 +13,29 @@
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __RARCH_CHEEVOS_MEMORY_H
|
||||
#define __RARCH_CHEEVOS_MEMORY_H
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_MENU_H
|
||||
#define __RARCH_CHEEVOS_MENU_H
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#define MAX_MEMORY_REGIONS 32
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t* data[MAX_MEMORY_REGIONS];
|
||||
size_t size[MAX_MEMORY_REGIONS];
|
||||
size_t total_size;
|
||||
unsigned count;
|
||||
} rcheevos_memory_regions_t;
|
||||
|
||||
bool rcheevos_memory_init(rcheevos_memory_regions_t* regions, int console);
|
||||
void rcheevos_memory_destroy(rcheevos_memory_regions_t* regions);
|
||||
|
||||
uint8_t* rcheevos_memory_find(const rcheevos_memory_regions_t* regions,
|
||||
unsigned address);
|
||||
void rcheevos_menu_populate(void* data);
|
||||
void rcheevos_menu_populate_hardcore_pause_submenu(void* data);
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char* buffer, size_t buffer_size);
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char* buffer, size_t buffer_size);
|
||||
uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
#endif /* HAVE_MENU */
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_MENU_H */
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "cheevos_parser.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "cheevos_locals.h"
|
||||
|
||||
#include <encodings/utf.h>
|
||||
#include <formats/rjson.h>
|
||||
|
||||
@@ -16,49 +16,10 @@
|
||||
#ifndef __RARCH_CHEEVOS_PARSER_H
|
||||
#define __RARCH_CHEEVOS_PARSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include "cheevos_locals.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* badge;
|
||||
const char* memaddr;
|
||||
unsigned points;
|
||||
unsigned id;
|
||||
unsigned active;
|
||||
} rcheevos_racheevo_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* title;
|
||||
const char* description;
|
||||
const char* mem;
|
||||
unsigned id;
|
||||
unsigned format;
|
||||
} rcheevos_ralboard_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* title;
|
||||
rcheevos_racheevo_t* core;
|
||||
rcheevos_racheevo_t* unofficial;
|
||||
rcheevos_ralboard_t* lboards;
|
||||
char* richpresence_script;
|
||||
|
||||
unsigned game_id;
|
||||
unsigned console_id;
|
||||
unsigned core_count;
|
||||
unsigned unofficial_count;
|
||||
unsigned lboard_count;
|
||||
} rcheevos_rapatchdata_t;
|
||||
|
||||
typedef void (*rcheevos_unlock_cb_t)(unsigned id, void* userdata);
|
||||
|
||||
int rcheevos_get_json_error(const char* json, char* token, size_t length);
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2016 - Andre Leiradella
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RARCH_CHEEVOS_UTIL_H
|
||||
#define __RARCH_CHEEVOS_UTIL_H
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/*****************************************************************************
|
||||
Setup - mainly for debugging
|
||||
*****************************************************************************/
|
||||
|
||||
/* Define this macro to get extra-verbose log for cheevos. */
|
||||
#define CHEEVOS_VERBOSE
|
||||
|
||||
/*****************************************************************************
|
||||
End of setup
|
||||
*****************************************************************************/
|
||||
|
||||
#define RCHEEVOS_TAG "[RCHEEVOS]: "
|
||||
#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q) free(q); } while (0)
|
||||
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
|
||||
#define CHEEVOS_LOG RARCH_LOG
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
|
||||
#else
|
||||
|
||||
#define CHEEVOS_LOG rcheevos_log
|
||||
#define CHEEVOS_ERR RARCH_ERR
|
||||
|
||||
void rcheevos_log(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif /* __RARCH_CHEEVOS_UTIL_H */
|
||||
@@ -157,6 +157,8 @@
|
||||
|
||||
#define DEFAULT_CRT_SWITCH_PORCH_ADJUST 0
|
||||
|
||||
#define DEFAULT_CRT_SWITCH_HIRES_MENU true
|
||||
|
||||
#define DEFAULT_HISTORY_LIST_ENABLE true
|
||||
|
||||
#define DEFAULT_PLAYLIST_ENTRY_RENAME true
|
||||
@@ -294,6 +296,7 @@
|
||||
* Can reduce latency at cost of higher risk of stuttering.
|
||||
*/
|
||||
#define DEFAULT_FRAME_DELAY 0
|
||||
#define MAXIMUM_FRAME_DELAY 19
|
||||
|
||||
/* Inserts black frame(s) inbetween frames.
|
||||
* Useful for Higher Hz monitors (set to multiples of 60 Hz) who want to play 60 Hz
|
||||
|
||||
@@ -195,7 +195,7 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, RETROK_UNKNOWN,
|
||||
RARCH_LIGHTGUN_TRIGGER, NO_BTN, NO_BTN, 0,
|
||||
RARCH_LIGHTGUN_TRIGGER, RETRO_DEVICE_ID_MOUSE_LEFT, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
@@ -750,7 +750,7 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, RETROK_UNKNOWN,
|
||||
RARCH_LIGHTGUN_TRIGGER, NO_BTN, NO_BTN, 0,
|
||||
RARCH_LIGHTGUN_TRIGGER, RETRO_DEVICE_ID_MOUSE_LEFT, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
@@ -1304,7 +1304,7 @@ static const struct retro_keybind retro_keybinds_1[] = {
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, RETROK_UNKNOWN,
|
||||
RARCH_LIGHTGUN_TRIGGER, NO_BTN, NO_BTN, 0,
|
||||
RARCH_LIGHTGUN_TRIGGER, RETRO_DEVICE_ID_MOUSE_LEFT, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
@@ -1872,7 +1872,7 @@ static const struct retro_keybind retro_keybinds_rest[] = {
|
||||
NULL, NULL,
|
||||
AXIS_NONE, AXIS_NONE, AXIS_NONE,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER, RETROK_UNKNOWN,
|
||||
RARCH_LIGHTGUN_TRIGGER, NO_BTN, NO_BTN, 0,
|
||||
RARCH_LIGHTGUN_TRIGGER, RETRO_DEVICE_ID_MOUSE_LEFT, NO_BTN, 0,
|
||||
true
|
||||
},
|
||||
{
|
||||
|
||||
+80
-21
@@ -213,6 +213,7 @@ enum bluetooth_driver_enum
|
||||
enum wifi_driver_enum
|
||||
{
|
||||
WIFI_CONNMANCTL = BLUETOOTH_NULL + 1,
|
||||
WIFI_NMCLI,
|
||||
WIFI_NULL
|
||||
};
|
||||
|
||||
@@ -340,7 +341,7 @@ static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_CTR;
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_SWITCH;
|
||||
#elif defined(HAVE_PULSE)
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_PULSE;
|
||||
#elif defined(HAVE_ALSA) && defined(HAVE_VIDEOCORE)
|
||||
#elif defined(HAVE_ALSA) && defined(HAVE_THREADS)
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_ALSATHREAD;
|
||||
#elif defined(HAVE_ALSA)
|
||||
static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_ALSA;
|
||||
@@ -1090,6 +1091,8 @@ const char *config_get_default_wifi(void)
|
||||
{
|
||||
case WIFI_CONNMANCTL:
|
||||
return "connmanctl";
|
||||
case WIFI_NMCLI:
|
||||
return "nmcli";
|
||||
case WIFI_NULL:
|
||||
break;
|
||||
}
|
||||
@@ -1457,6 +1460,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("frame_time_counter_reset_after_load_state", &settings->bools.frame_time_counter_reset_after_load_state, true, false, false);
|
||||
SETTING_BOOL("frame_time_counter_reset_after_save_state", &settings->bools.frame_time_counter_reset_after_save_state, true, false, false);
|
||||
SETTING_BOOL("crt_switch_resolution_use_custom_refresh_rate", &settings->bools.crt_switch_custom_refresh_enable, true, false, false);
|
||||
SETTING_BOOL("crt_switch_hires_menu", &settings->bools.crt_switch_hires_menu, true, false, true);
|
||||
SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false);
|
||||
SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false);
|
||||
SETTING_BOOL("ui_companion_toggle", &settings->bools.ui_companion_toggle, false, ui_companion_toggle, false);
|
||||
@@ -1739,6 +1743,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("cheevos_enable", &settings->bools.cheevos_enable, true, DEFAULT_CHEEVOS_ENABLE, false);
|
||||
SETTING_BOOL("cheevos_test_unofficial", &settings->bools.cheevos_test_unofficial, true, false, false);
|
||||
SETTING_BOOL("cheevos_hardcore_mode_enable", &settings->bools.cheevos_hardcore_mode_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_challenge_indicators", &settings->bools.cheevos_challenge_indicators, true, true, false);
|
||||
SETTING_BOOL("cheevos_richpresence_enable", &settings->bools.cheevos_richpresence_enable, true, true, false);
|
||||
SETTING_BOOL("cheevos_unlock_sound_enable", &settings->bools.cheevos_unlock_sound_enable, true, false, false);
|
||||
SETTING_BOOL("cheevos_verbose_enable", &settings->bools.cheevos_verbose_enable, true, false, false);
|
||||
@@ -2430,10 +2435,8 @@ void config_set_defaults(void *data)
|
||||
#endif
|
||||
|
||||
input_config_reset();
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
input_remapping_deinit();
|
||||
input_remapping_set_defaults();
|
||||
#endif
|
||||
input_remapping_set_defaults(false);
|
||||
|
||||
/* Verify that binds are in proper order. */
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
@@ -2456,7 +2459,7 @@ void config_set_defaults(void *data)
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
settings->uints.input_joypad_map[i] = i;
|
||||
settings->uints.input_joypad_index[i] = i;
|
||||
#ifdef SWITCH /* Switch prefered default dpad mode */
|
||||
settings->uints.input_analog_dpad_mode[i] = ANALOG_DPAD_LSTICK;
|
||||
#else
|
||||
@@ -3191,7 +3194,7 @@ static bool config_load_file(global_t *global,
|
||||
buf[0] = '\0';
|
||||
|
||||
snprintf(buf, sizeof(buf), "input_player%u_joypad_index", i + 1);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_joypad_map[i], buf);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_joypad_index[i], buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "input_player%u_analog_dpad_mode", i + 1);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_analog_dpad_mode[i], buf);
|
||||
@@ -3275,8 +3278,8 @@ static bool config_load_file(global_t *global,
|
||||
if (settings->uints.video_hard_sync_frames > 3)
|
||||
settings->uints.video_hard_sync_frames = 3;
|
||||
|
||||
if (settings->uints.video_frame_delay > 15)
|
||||
settings->uints.video_frame_delay = 15;
|
||||
if (settings->uints.video_frame_delay > MAXIMUM_FRAME_DELAY)
|
||||
settings->uints.video_frame_delay = MAXIMUM_FRAME_DELAY;
|
||||
|
||||
settings->uints.video_swap_interval = MAX(settings->uints.video_swap_interval, 1);
|
||||
settings->uints.video_swap_interval = MIN(settings->uints.video_swap_interval, 4);
|
||||
@@ -3851,9 +3854,7 @@ bool config_load_remap(const char *directory_input_remapping,
|
||||
FILE_PATH_REMAP_EXTENSION,
|
||||
sizeof(game_path));
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
input_remapping_set_defaults();
|
||||
#endif
|
||||
input_remapping_set_defaults(false);
|
||||
|
||||
/* If a game remap file exists, load it. */
|
||||
if ((new_conf = config_file_new_from_path_to_string(game_path)))
|
||||
@@ -4224,7 +4225,7 @@ bool config_save_file(const char *path)
|
||||
snprintf(cfg, sizeof(cfg), "input_device_p%u", i + 1);
|
||||
config_set_int(conf, cfg, settings->uints.input_device[i]);
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_joypad_index", i + 1);
|
||||
config_set_int(conf, cfg, settings->uints.input_joypad_map[i]);
|
||||
config_set_int(conf, cfg, settings->uints.input_joypad_index[i]);
|
||||
snprintf(cfg, sizeof(cfg), "input_libretro_device_p%u", i + 1);
|
||||
config_set_int(conf, cfg, input_config_get_device(i));
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_analog_dpad_mode", i + 1);
|
||||
@@ -4487,11 +4488,11 @@ bool config_save_overrides(enum override_type type, void *data)
|
||||
config_set_int(conf, cfg, overrides->uints.input_device[i]);
|
||||
}
|
||||
|
||||
if (settings->uints.input_joypad_map[i]
|
||||
!= overrides->uints.input_joypad_map[i])
|
||||
if (settings->uints.input_joypad_index[i]
|
||||
!= overrides->uints.input_joypad_index[i])
|
||||
{
|
||||
snprintf(cfg, sizeof(cfg), "input_player%u_joypad_index", i + 1);
|
||||
config_set_int(conf, cfg, overrides->uints.input_joypad_map[i]);
|
||||
config_set_int(conf, cfg, overrides->uints.input_joypad_index[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4604,7 +4605,7 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
if (!string_is_empty(global->name.remapfile))
|
||||
{
|
||||
input_remapping_deinit();
|
||||
input_remapping_set_defaults();
|
||||
input_remapping_set_defaults(false);
|
||||
}
|
||||
global->name.remapfile = strdup(path);
|
||||
|
||||
@@ -4612,9 +4613,6 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
{
|
||||
char s1[32], s2[32], s3[32];
|
||||
|
||||
global->old_analog_dpad_mode[i] = settings->uints.input_analog_dpad_mode[i];
|
||||
global->old_libretro_device[i] = settings->uints.input_libretro_device[i];
|
||||
|
||||
s1[0] = '\0';
|
||||
s2[0] = '\0';
|
||||
s3[0] = '\0';
|
||||
@@ -4659,9 +4657,12 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
else
|
||||
{
|
||||
char stk_ident[128];
|
||||
char key_ident[128];
|
||||
int stk_remap = -1;
|
||||
int key_remap = -1;
|
||||
|
||||
stk_ident[0] = '\0';
|
||||
key_ident[0] = '\0';
|
||||
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
key_string, '$', sizeof(stk_ident));
|
||||
@@ -4680,6 +4681,15 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_remap_ids[i][j], stk_remap);
|
||||
}
|
||||
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
|
||||
if (!config_get_int(conf, key_ident, &key_remap))
|
||||
key_remap = RETROK_UNKNOWN;
|
||||
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.input_keymapper_ids[i][j], key_remap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4688,8 +4698,18 @@ bool input_remapping_load_file(void *data, const char *path)
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_libretro_device_p%u", i + 1);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_libretro_device[i], s1);
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_remap_port_p%u", i + 1);
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.input_remap_ports[i], s1);
|
||||
}
|
||||
|
||||
input_remapping_update_port_map();
|
||||
|
||||
/* Whenever a remap file is loaded, subsequent
|
||||
* changes to global remap-related parameters
|
||||
* must be reset at the next core deinitialisation */
|
||||
input_remapping_enable_global_config_restore();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4728,14 +4748,38 @@ bool input_remapping_save_file(const char *path)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < max_users; i++)
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
char s1[32], s2[32], s3[32];
|
||||
bool skip_port = true;
|
||||
char s1[32];
|
||||
char s2[32];
|
||||
char s3[32];
|
||||
|
||||
s1[0] = '\0';
|
||||
s2[0] = '\0';
|
||||
s3[0] = '\0';
|
||||
|
||||
/* We must include all mapped ports + all those
|
||||
* with an index less than max_users */
|
||||
if (i < max_users)
|
||||
skip_port = false;
|
||||
else
|
||||
{
|
||||
/* Check whether current port is mapped
|
||||
* to an input device */
|
||||
for (j = 0; j < max_users; j++)
|
||||
{
|
||||
if (i == settings->uints.input_remap_ports[j])
|
||||
{
|
||||
skip_port = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skip_port)
|
||||
continue;
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_player%u_btn", i + 1);
|
||||
snprintf(s2, sizeof(s2), "input_player%u_key", i + 1);
|
||||
snprintf(s3, sizeof(s3), "input_player%u_stk", i + 1);
|
||||
@@ -4777,6 +4821,7 @@ bool input_remapping_save_file(const char *path)
|
||||
{
|
||||
char stk_ident[128];
|
||||
unsigned remap_id = settings->uints.input_remap_ids[i][j];
|
||||
unsigned keymap_id = settings->uints.input_keymapper_ids[i][j];
|
||||
const char *key_string = key_strings[j];
|
||||
stk_ident[0] = '\0';
|
||||
fill_pathname_join_delim(stk_ident, s3,
|
||||
@@ -4793,12 +4838,26 @@ bool input_remapping_save_file(const char *path)
|
||||
config_set_int(conf, stk_ident,
|
||||
settings->uints.input_remap_ids[i][j]);
|
||||
}
|
||||
|
||||
if (keymap_id != RETROK_UNKNOWN)
|
||||
{
|
||||
char key_ident[128];
|
||||
key_ident[0] = '\0';
|
||||
fill_pathname_join_delim(key_ident, s2,
|
||||
key_string, '_', sizeof(key_ident));
|
||||
config_set_int(conf, key_ident,
|
||||
settings->uints.input_keymapper_ids[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_libretro_device_p%u", i + 1);
|
||||
config_set_int(conf, s1, input_config_get_device(i));
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_player%u_analog_dpad_mode", i + 1);
|
||||
config_set_int(conf, s1, settings->uints.input_analog_dpad_mode[i]);
|
||||
|
||||
snprintf(s1, sizeof(s1), "input_remap_port_p%u", i + 1);
|
||||
config_set_int(conf, s1, settings->uints.input_remap_ports[i]);
|
||||
}
|
||||
|
||||
ret = config_file_write(conf, remap_file, true);
|
||||
|
||||
+9
-4
@@ -67,7 +67,9 @@ enum crt_switch_type
|
||||
{
|
||||
CRT_SWITCH_NONE = 0,
|
||||
CRT_SWITCH_15KHZ,
|
||||
CRT_SWITCH_31KHZ
|
||||
CRT_SWITCH_31KHZ,
|
||||
CRT_SWITCH_32_120,
|
||||
CRT_SWITCH_INI
|
||||
};
|
||||
|
||||
enum override_type
|
||||
@@ -120,7 +122,7 @@ typedef struct settings
|
||||
unsigned placeholder;
|
||||
|
||||
unsigned input_split_joycon[MAX_USERS];
|
||||
unsigned input_joypad_map[MAX_USERS];
|
||||
unsigned input_joypad_index[MAX_USERS];
|
||||
unsigned input_device[MAX_USERS];
|
||||
unsigned input_mouse_index[MAX_USERS];
|
||||
/* Set by autoconfiguration in joypad_autoconfig_dir.
|
||||
@@ -128,9 +130,10 @@ typedef struct settings
|
||||
unsigned input_libretro_device[MAX_USERS];
|
||||
unsigned input_analog_dpad_mode[MAX_USERS];
|
||||
|
||||
unsigned input_keymapper_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END];
|
||||
|
||||
unsigned input_remap_ports[MAX_USERS];
|
||||
unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END];
|
||||
unsigned input_keymapper_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END];
|
||||
unsigned input_remap_port_map[MAX_USERS][MAX_USERS + 1];
|
||||
|
||||
unsigned led_map[MAX_LEDS];
|
||||
|
||||
@@ -697,6 +700,7 @@ typedef struct settings
|
||||
bool kiosk_mode_enable;
|
||||
|
||||
bool crt_switch_custom_refresh_enable;
|
||||
bool crt_switch_hires_menu;
|
||||
|
||||
/* Netplay */
|
||||
bool netplay_public_announce;
|
||||
@@ -732,6 +736,7 @@ typedef struct settings
|
||||
bool cheevos_auto_screenshot;
|
||||
bool cheevos_start_active;
|
||||
bool cheevos_unlock_sound_enable;
|
||||
bool cheevos_challenge_indicators;
|
||||
|
||||
/* Camera */
|
||||
bool camera_allow;
|
||||
|
||||
@@ -124,6 +124,10 @@ bool content_set_subsystem_by_name(const char* subsystem_name);
|
||||
/* Get the current subsystem "friendly name" */
|
||||
void content_get_subsystem_friendly_name(const char* subsystem_name, char* subsystem_friendly_name, size_t len);
|
||||
|
||||
/* Sets overrides which modify frontend handling of
|
||||
* specific content file types */
|
||||
bool content_file_override_set(const struct retro_system_content_info_override *overrides);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
Vendored
+4
-4
@@ -144,7 +144,7 @@ int x86cpuid_GetFirm(const Cx86cpuid *p)
|
||||
return -1;
|
||||
}
|
||||
|
||||
BoolInt CPU_Is_InOrder()
|
||||
BoolInt CPU_Is_InOrder(void)
|
||||
{
|
||||
Cx86cpuid p;
|
||||
int firm;
|
||||
@@ -175,7 +175,7 @@ BoolInt CPU_Is_InOrder()
|
||||
|
||||
#if !defined(MY_CPU_AMD64) && defined(_WIN32)
|
||||
#include <windows.h>
|
||||
static BoolInt CPU_Sys_Is_SSE_Supported()
|
||||
static BoolInt CPU_Sys_Is_SSE_Supported(void)
|
||||
{
|
||||
OSVERSIONINFO vi;
|
||||
vi.dwOSVersionInfoSize = sizeof(vi);
|
||||
@@ -188,7 +188,7 @@ static BoolInt CPU_Sys_Is_SSE_Supported()
|
||||
#define CHECK_SYS_SSE_SUPPORT
|
||||
#endif
|
||||
|
||||
BoolInt CPU_Is_Aes_Supported()
|
||||
BoolInt CPU_Is_Aes_Supported(void)
|
||||
{
|
||||
Cx86cpuid p;
|
||||
CHECK_SYS_SSE_SUPPORT
|
||||
@@ -197,7 +197,7 @@ BoolInt CPU_Is_Aes_Supported()
|
||||
return (p.c >> 25) & 1;
|
||||
}
|
||||
|
||||
BoolInt CPU_IsSupported_PageGB()
|
||||
BoolInt CPU_IsSupported_PageGB(void)
|
||||
{
|
||||
Cx86cpuid cpuid;
|
||||
if (!x86cpuid_CheckAndRead(&cpuid))
|
||||
|
||||
Vendored
+3
-3
@@ -325,9 +325,9 @@ int x86cpuid_GetFirm(const Cx86cpuid *p);
|
||||
#define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF))
|
||||
#define x86cpuid_GetStepping(ver) (ver & 0xF)
|
||||
|
||||
BoolInt CPU_Is_InOrder();
|
||||
BoolInt CPU_Is_Aes_Supported();
|
||||
BoolInt CPU_IsSupported_PageGB();
|
||||
BoolInt CPU_Is_InOrder(void);
|
||||
BoolInt CPU_Is_Aes_Supported(void);
|
||||
BoolInt CPU_IsSupported_PageGB(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Vendored
+82
-60
@@ -43,6 +43,7 @@ extern const SceGxmProgram texture_tint_f_gxp;
|
||||
|
||||
static vita2d_video_mode_data video_mode_data;
|
||||
static vita2d_video_mode video_mode_initial;
|
||||
static SceGxmMultisampleMode current_msaa = SCE_GXM_MULTISAMPLE_4X;
|
||||
|
||||
static int pgf_module_was_loaded = 0;
|
||||
|
||||
@@ -157,6 +158,8 @@ vita2d_video_mode_data vita2d_get_video_mode_data(){
|
||||
}
|
||||
|
||||
static int vita2d_switch_video_mode(vita2d_video_mode video_mode){
|
||||
int i,x,y,err;
|
||||
|
||||
if(video_mode > video_mode_initial)
|
||||
return -1;
|
||||
switch (video_mode)
|
||||
@@ -164,7 +167,7 @@ static int vita2d_switch_video_mode(vita2d_video_mode video_mode){
|
||||
case VITA2D_VIDEO_MODE_960x544:
|
||||
video_mode_data.width = 960;
|
||||
video_mode_data.height = 544;
|
||||
video_mode_data.stride = 1280;
|
||||
video_mode_data.stride = 960;
|
||||
break;
|
||||
|
||||
case VITA2D_VIDEO_MODE_1280x720:
|
||||
@@ -181,6 +184,65 @@ static int vita2d_switch_video_mode(vita2d_video_mode video_mode){
|
||||
clip_rect_x_max = video_mode_data.width;
|
||||
clip_rect_y_max = video_mode_data.height;
|
||||
|
||||
if(renderTarget != NULL){
|
||||
sceGxmDestroyRenderTarget(renderTarget);
|
||||
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// clear the buffer then deallocate
|
||||
memset(displayBufferData[i], 0, video_mode_data.height*video_mode_data.stride*4);
|
||||
gpu_free(displayBufferUid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// allocate memory and sync objects for display buffers
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// allocate memory for display
|
||||
displayBufferData[i] = gpu_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
4*video_mode_data.stride*video_mode_data.height,
|
||||
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&displayBufferUid[i]);
|
||||
|
||||
// memset the buffer to black
|
||||
for (y = 0; y < video_mode_data.height; y++) {
|
||||
unsigned int *row = (unsigned int *)displayBufferData[i] + y*video_mode_data.stride;
|
||||
for (x = 0; x < video_mode_data.width; x++) {
|
||||
row[x] = 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize a color surface for this display buffer
|
||||
err = sceGxmColorSurfaceInit(
|
||||
&displaySurface[i],
|
||||
DISPLAY_COLOR_FORMAT,
|
||||
SCE_GXM_COLOR_SURFACE_LINEAR,
|
||||
(current_msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
||||
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
||||
video_mode_data.width,
|
||||
video_mode_data.height,
|
||||
video_mode_data.stride,
|
||||
displayBufferData[i]);
|
||||
|
||||
}
|
||||
|
||||
// set up parameters
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = video_mode_data.width;
|
||||
renderTargetParams.height = video_mode_data.height;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = current_msaa;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
renderTargetParams.driverMemBlock = -1; // Invalid UID
|
||||
|
||||
// create the render target
|
||||
err = sceGxmCreateRenderTarget(&renderTargetParams, &renderTarget);
|
||||
VITA2D_DEBUG("sceGxmCreateRenderTarget(): 0x%08X\n", err);
|
||||
|
||||
|
||||
|
||||
matrix_init_orthographic(_vita2d_ortho_matrix, 0.0f, video_mode_data.width, video_mode_data.height, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
return 0;
|
||||
@@ -198,7 +260,7 @@ static void display_callback(const void *callback_data)
|
||||
framebuf.pixelformat = DISPLAY_PIXEL_FORMAT;
|
||||
framebuf.width = video_mode_data.width;
|
||||
framebuf.height = video_mode_data.height;
|
||||
if(sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME)<0){
|
||||
if(sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME) == SCE_DISPLAY_ERROR_INVALID_RESOLUTION){
|
||||
if(video_mode_initial)
|
||||
vita2d_switch_video_mode(VITA2D_VIDEO_MODE_960x544);
|
||||
}
|
||||
@@ -258,7 +320,7 @@ static void _vita2d_make_fragment_programs(vita2d_fragment_programs *out,
|
||||
static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMode msaa, vita2d_video_mode video_mode)
|
||||
{
|
||||
int err;
|
||||
unsigned int i, x, y;
|
||||
unsigned int i;
|
||||
UNUSED(err);
|
||||
|
||||
if (vita2d_initialized) {
|
||||
@@ -267,12 +329,7 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
}
|
||||
|
||||
video_mode_initial = video_mode;
|
||||
|
||||
err = vita2d_switch_video_mode(video_mode);
|
||||
if(err<0){
|
||||
VITA2D_DEBUG("vita2d_switch_video_mode(): 0x%08X\n", err);
|
||||
return err;
|
||||
}
|
||||
current_msaa = msaa;
|
||||
|
||||
SceGxmInitializeParams initializeParams;
|
||||
memset(&initializeParams, 0, sizeof(SceGxmInitializeParams));
|
||||
@@ -329,51 +386,18 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
err = sceGxmCreateContext(&contextParams, &_vita2d_context);
|
||||
VITA2D_DEBUG("sceGxmCreateContext(): 0x%08X\n", err);
|
||||
|
||||
// set up parameters
|
||||
SceGxmRenderTargetParams renderTargetParams;
|
||||
memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams));
|
||||
renderTargetParams.flags = 0;
|
||||
renderTargetParams.width = video_mode_data.width;
|
||||
renderTargetParams.height = video_mode_data.height;
|
||||
renderTargetParams.scenesPerFrame = 1;
|
||||
renderTargetParams.multisampleMode = msaa;
|
||||
renderTargetParams.multisampleLocations = 0;
|
||||
renderTargetParams.driverMemBlock = -1; // Invalid UID
|
||||
err = vita2d_switch_video_mode(video_mode);
|
||||
if(err<0){
|
||||
VITA2D_DEBUG("vita2d_switch_video_mode(): 0x%08X\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// create the render target
|
||||
err = sceGxmCreateRenderTarget(&renderTargetParams, &renderTarget);
|
||||
VITA2D_DEBUG("sceGxmCreateRenderTarget(): 0x%08X\n", err);
|
||||
vita2d_display_data displayData;
|
||||
displayData.address = displayBufferData[0];
|
||||
display_callback(&displayData);
|
||||
|
||||
// allocate memory and sync objects for display buffers
|
||||
for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
|
||||
// allocate memory for display
|
||||
displayBufferData[i] = gpu_alloc(
|
||||
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
|
||||
4*video_mode_data.stride*video_mode_data.height,
|
||||
SCE_GXM_COLOR_SURFACE_ALIGNMENT,
|
||||
SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE,
|
||||
&displayBufferUid[i]);
|
||||
|
||||
// memset the buffer to black
|
||||
for (y = 0; y < video_mode_data.height; y++) {
|
||||
unsigned int *row = (unsigned int *)displayBufferData[i] + y*video_mode_data.stride;
|
||||
for (x = 0; x < video_mode_data.width; x++) {
|
||||
row[x] = 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize a color surface for this display buffer
|
||||
err = sceGxmColorSurfaceInit(
|
||||
&displaySurface[i],
|
||||
DISPLAY_COLOR_FORMAT,
|
||||
SCE_GXM_COLOR_SURFACE_LINEAR,
|
||||
(msaa == SCE_GXM_MULTISAMPLE_NONE) ? SCE_GXM_COLOR_SURFACE_SCALE_NONE : SCE_GXM_COLOR_SURFACE_SCALE_MSAA_DOWNSCALE,
|
||||
SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,
|
||||
video_mode_data.width,
|
||||
video_mode_data.height,
|
||||
video_mode_data.stride,
|
||||
displayBufferData[i]);
|
||||
|
||||
// create a sync object that we will associate with this buffer
|
||||
err = sceGxmSyncObjectCreate(&displayBufferSync[i]);
|
||||
}
|
||||
@@ -383,11 +407,11 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
const unsigned int alignedHeight = ALIGN(video_mode_data.height, SCE_GXM_TILE_SIZEY);
|
||||
unsigned int sampleCount = alignedWidth*alignedHeight;
|
||||
unsigned int depthStrideInSamples = alignedWidth;
|
||||
if (msaa == SCE_GXM_MULTISAMPLE_4X) {
|
||||
if (current_msaa == SCE_GXM_MULTISAMPLE_4X) {
|
||||
// samples increase in X and Y
|
||||
sampleCount *= 4;
|
||||
depthStrideInSamples *= 2;
|
||||
} else if (msaa == SCE_GXM_MULTISAMPLE_2X) {
|
||||
} else if (current_msaa == SCE_GXM_MULTISAMPLE_2X) {
|
||||
// samples increase in Y only
|
||||
sampleCount *= 2;
|
||||
}
|
||||
@@ -572,7 +596,7 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
shaderPatcher,
|
||||
clearFragmentProgramId,
|
||||
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
|
||||
msaa,
|
||||
current_msaa,
|
||||
NULL,
|
||||
clearVertexProgramGxp,
|
||||
&clearFragmentProgram);
|
||||
@@ -731,8 +755,8 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
VITA2D_DEBUG("texture sceGxmShaderPatcherCreateVertexProgram(): 0x%08X\n", err);
|
||||
|
||||
// Create variations of the fragment program based on blending mode
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_normal, &blend_info, msaa);
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_add, &blend_info_add, msaa);
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_normal, &blend_info, current_msaa);
|
||||
_vita2d_make_fragment_programs(&_vita2d_fragmentPrograms.blend_mode_add, &blend_info_add, current_msaa);
|
||||
|
||||
// Default to "normal" blending mode (non-additive)
|
||||
vita2d_set_blend_mode_add(0);
|
||||
@@ -770,10 +794,6 @@ static int vita2d_init_internal(unsigned int temp_pool_size, SceGxmMultisampleMo
|
||||
if (pgf_module_was_loaded != SCE_SYSMODULE_LOADED)
|
||||
sceSysmoduleLoadModule(SCE_SYSMODULE_PGF);
|
||||
|
||||
vita2d_display_data displayData;
|
||||
displayData.address = displayBufferData[backBufferIndex];
|
||||
display_callback(&displayData);
|
||||
|
||||
vita2d_initialized = 1;
|
||||
return 1;
|
||||
}
|
||||
@@ -795,7 +815,8 @@ int vita2d_init_advanced_with_msaa(unsigned int temp_pool_size, SceGxmMultisampl
|
||||
|
||||
void vita2d_wait_rendering_done()
|
||||
{
|
||||
sceGxmFinish(_vita2d_context);
|
||||
if(vita2d_initialized)
|
||||
sceGxmFinish(_vita2d_context);
|
||||
}
|
||||
|
||||
int vita2d_fini()
|
||||
@@ -857,6 +878,7 @@ int vita2d_fini()
|
||||
|
||||
// destroy the render target
|
||||
sceGxmDestroyRenderTarget(renderTarget);
|
||||
renderTarget = NULL;
|
||||
|
||||
// destroy the _vita2d_context
|
||||
sceGxmDestroyContext(_vita2d_context);
|
||||
|
||||
Vendored
-476
@@ -1,476 +0,0 @@
|
||||
PTHREADS-WIN32 RELEASE 2.8.0 (2006-12-22)
|
||||
-----------------------------------------
|
||||
Web Site: http://sources.redhat.com/pthreads-win32/
|
||||
FTP Site: ftp://sources.redhat.com/pub/pthreads-win32
|
||||
Maintainer: Ross Johnson <[email protected]>
|
||||
|
||||
|
||||
We are pleased to announce the availability of a new release of
|
||||
Pthreads-win32, an Open Source Software implementation of the
|
||||
Threads component of the POSIX 1003.1 2001 Standard for Microsoft's
|
||||
Win32 environment. Some functions from other sections of POSIX
|
||||
1003.1 2001 are also supported including semaphores and scheduling
|
||||
functions.
|
||||
|
||||
Some common non-portable functions are also implemented for
|
||||
additional compatibility, as are a few functions specific
|
||||
to pthreads-win32 for easier integration with Win32 applications.
|
||||
|
||||
Pthreads-win32 is free software, distributed under the GNU Lesser
|
||||
General Public License (LGPL).
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
This library is based originally on a Win32 pthreads
|
||||
implementation contributed by John Bossom <[email protected]>.
|
||||
|
||||
The implementation of Condition Variables uses algorithms developed
|
||||
by Alexander Terekhov and Louis Thomas.
|
||||
|
||||
The implementation of POSIX mutexes has been improved by Thomas Pfaff
|
||||
and later by Alexander Terekhov.
|
||||
|
||||
The implementation of Spinlocks and Barriers was contributed
|
||||
by Ross Johnson.
|
||||
|
||||
The implementation of read/write locks was contributed by
|
||||
Aurelio Medina and improved by Alexander Terekhov.
|
||||
|
||||
Many others have contributed significant time and effort to solve crutial
|
||||
problems in order to make the library workable, robust and reliable.
|
||||
|
||||
Thanks to Xavier Leroy for granting permission to use and modify his
|
||||
LinuxThreads manual pages.
|
||||
|
||||
Thanks to The Open Group for making the Single Unix Specification
|
||||
publicly available - many of the manual pages included in the package
|
||||
were extracted from it.
|
||||
|
||||
There is also a separate CONTRIBUTORS file. This file and others are
|
||||
on the web site:
|
||||
|
||||
http://sources.redhat.com/pthreads-win32
|
||||
|
||||
As much as possible, the ChangeLog file acknowledges contributions to the
|
||||
code base in more detail.
|
||||
|
||||
|
||||
Changes since the last release
|
||||
------------------------------
|
||||
These are now documented in the NEWS file.
|
||||
See the ChangeLog file also.
|
||||
|
||||
|
||||
Known Bugs
|
||||
----------
|
||||
These are now documented in the BUGS file.
|
||||
|
||||
|
||||
Level of standards conformance
|
||||
------------------------------
|
||||
|
||||
The following POSIX 1003.1 2001 options are defined and set to 200112L:
|
||||
|
||||
_POSIX_THREADS
|
||||
_POSIX_THREAD_SAFE_FUNCTIONS
|
||||
_POSIX_THREAD_ATTR_STACKSIZE
|
||||
_POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
_POSIX_SEMAPHORES
|
||||
_POSIX_READER_WRITER_LOCKS
|
||||
_POSIX_SPIN_LOCKS
|
||||
_POSIX_BARRIERS
|
||||
|
||||
|
||||
The following POSIX 1003.1 2001 options are defined and set to -1:
|
||||
|
||||
_POSIX_THREAD_ATTR_STACKADDR
|
||||
_POSIX_THREAD_PRIO_INHERIT
|
||||
_POSIX_THREAD_PRIO_PROTECT
|
||||
_POSIX_THREAD_PROCESS_SHARED
|
||||
|
||||
|
||||
The following POSIX 1003.1 2001 limits are defined and set:
|
||||
|
||||
_POSIX_THREAD_THREADS_MAX
|
||||
_POSIX_SEM_VALUE_MAX
|
||||
_POSIX_SEM_NSEMS_MAX
|
||||
_POSIX_THREAD_KEYS_MAX
|
||||
_POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
PTHREAD_STACK_MIN
|
||||
PTHREAD_THREADS_MAX
|
||||
SEM_VALUE_MAX
|
||||
SEM_NSEMS_MAX
|
||||
PTHREAD_KEYS_MAX
|
||||
PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
|
||||
|
||||
The following functions are implemented:
|
||||
|
||||
---------------------------
|
||||
PThreads
|
||||
---------------------------
|
||||
pthread_attr_init
|
||||
pthread_attr_destroy
|
||||
pthread_attr_getdetachstate
|
||||
pthread_attr_getstackaddr
|
||||
pthread_attr_getstacksize
|
||||
pthread_attr_setdetachstate
|
||||
pthread_attr_setstackaddr
|
||||
pthread_attr_setstacksize
|
||||
|
||||
pthread_create
|
||||
pthread_detach
|
||||
pthread_equal
|
||||
pthread_exit
|
||||
pthread_join
|
||||
pthread_once
|
||||
pthread_self
|
||||
|
||||
pthread_cancel
|
||||
pthread_cleanup_pop
|
||||
pthread_cleanup_push
|
||||
pthread_setcancelstate
|
||||
pthread_setcanceltype
|
||||
pthread_testcancel
|
||||
|
||||
---------------------------
|
||||
Thread Specific Data
|
||||
---------------------------
|
||||
pthread_key_create
|
||||
pthread_key_delete
|
||||
pthread_setspecific
|
||||
pthread_getspecific
|
||||
|
||||
---------------------------
|
||||
Mutexes
|
||||
---------------------------
|
||||
pthread_mutexattr_init
|
||||
pthread_mutexattr_destroy
|
||||
pthread_mutexattr_getpshared
|
||||
pthread_mutexattr_setpshared
|
||||
pthread_mutexattr_gettype
|
||||
pthread_mutexattr_settype (types: PTHREAD_MUTEX_DEFAULT
|
||||
PTHREAD_MUTEX_NORMAL
|
||||
PTHREAD_MUTEX_ERRORCHECK
|
||||
PTHREAD_MUTEX_RECURSIVE )
|
||||
pthread_mutex_init
|
||||
pthread_mutex_destroy
|
||||
pthread_mutex_lock
|
||||
pthread_mutex_trylock
|
||||
pthread_mutex_timedlock
|
||||
pthread_mutex_unlock
|
||||
|
||||
---------------------------
|
||||
Condition Variables
|
||||
---------------------------
|
||||
pthread_condattr_init
|
||||
pthread_condattr_destroy
|
||||
pthread_condattr_getpshared
|
||||
pthread_condattr_setpshared
|
||||
|
||||
pthread_cond_init
|
||||
pthread_cond_destroy
|
||||
pthread_cond_wait
|
||||
pthread_cond_timedwait
|
||||
pthread_cond_signal
|
||||
pthread_cond_broadcast
|
||||
|
||||
---------------------------
|
||||
Read/Write Locks
|
||||
---------------------------
|
||||
pthread_rwlock_init
|
||||
pthread_rwlock_destroy
|
||||
pthread_rwlock_tryrdlock
|
||||
pthread_rwlock_trywrlock
|
||||
pthread_rwlock_rdlock
|
||||
pthread_rwlock_timedrdlock
|
||||
pthread_rwlock_rwlock
|
||||
pthread_rwlock_timedwrlock
|
||||
pthread_rwlock_unlock
|
||||
pthread_rwlockattr_init
|
||||
pthread_rwlockattr_destroy
|
||||
pthread_rwlockattr_getpshared
|
||||
pthread_rwlockattr_setpshared
|
||||
|
||||
---------------------------
|
||||
Spin Locks
|
||||
---------------------------
|
||||
pthread_spin_init
|
||||
pthread_spin_destroy
|
||||
pthread_spin_lock
|
||||
pthread_spin_unlock
|
||||
pthread_spin_trylock
|
||||
|
||||
---------------------------
|
||||
Barriers
|
||||
---------------------------
|
||||
pthread_barrier_init
|
||||
pthread_barrier_destroy
|
||||
pthread_barrier_wait
|
||||
pthread_barrierattr_init
|
||||
pthread_barrierattr_destroy
|
||||
pthread_barrierattr_getpshared
|
||||
pthread_barrierattr_setpshared
|
||||
|
||||
---------------------------
|
||||
Semaphores
|
||||
---------------------------
|
||||
sem_init
|
||||
sem_destroy
|
||||
sem_post
|
||||
sem_wait
|
||||
sem_trywait
|
||||
sem_timedwait
|
||||
sem_getvalue (# free if +ve, # of waiters if -ve)
|
||||
sem_open (returns an error ENOSYS)
|
||||
sem_close (returns an error ENOSYS)
|
||||
sem_unlink (returns an error ENOSYS)
|
||||
|
||||
---------------------------
|
||||
RealTime Scheduling
|
||||
---------------------------
|
||||
pthread_attr_getschedparam
|
||||
pthread_attr_setschedparam
|
||||
pthread_attr_getinheritsched
|
||||
pthread_attr_setinheritsched
|
||||
pthread_attr_getschedpolicy (only supports SCHED_OTHER)
|
||||
pthread_attr_setschedpolicy (only supports SCHED_OTHER)
|
||||
pthread_getschedparam
|
||||
pthread_setschedparam
|
||||
pthread_getconcurrency
|
||||
pthread_setconcurrency
|
||||
pthread_attr_getscope
|
||||
pthread_attr_setscope (only supports PTHREAD_SCOPE_SYSTEM)
|
||||
sched_get_priority_max
|
||||
sched_get_priority_min
|
||||
sched_rr_get_interval (returns an error ENOTSUP)
|
||||
sched_setscheduler (only supports SCHED_OTHER)
|
||||
sched_getscheduler (only supports SCHED_OTHER)
|
||||
sched_yield
|
||||
|
||||
---------------------------
|
||||
Signals
|
||||
---------------------------
|
||||
pthread_sigmask
|
||||
pthread_kill (only supports zero sig value,
|
||||
for thread validity checking)
|
||||
|
||||
---------------------------
|
||||
Non-portable routines (see the README.NONPORTABLE file for usage)
|
||||
---------------------------
|
||||
pthread_getw32threadhandle_np
|
||||
pthread_timechange_handler_np
|
||||
pthread_delay_np
|
||||
pthread_mutexattr_getkind_np
|
||||
pthread_mutexattr_setkind_np (types: PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||
PTHREAD_MUTEX_RECURSIVE_NP,
|
||||
PTHREAD_MUTEX_ADAPTIVE_NP,
|
||||
PTHREAD_MUTEX_TIMED_NP)
|
||||
pthread_num_processors_np
|
||||
pthread_win32_process_attach_np (Required when statically linking
|
||||
the library)
|
||||
pthread_win32_process_detach_np (Required when statically linking
|
||||
the library)
|
||||
pthread_win32_thread_attach_np (Required when statically linking
|
||||
the library)
|
||||
pthread_win32_thread_detach_np (Required when statically linking
|
||||
the library)
|
||||
|
||||
---------------------------
|
||||
Static Initializers
|
||||
---------------------------
|
||||
PTHREAD_ONCE_INIT
|
||||
PTHREAD_MUTEX_INITIALIZER
|
||||
PTHREAD_RECURSIVE_MUTEX_INITIALIZER
|
||||
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
|
||||
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
|
||||
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
|
||||
PTHREAD_COND_INITIALIZER
|
||||
PTHREAD_RWLOCK_INITIALIZER
|
||||
PTHREAD_SPINLOCK_INITIALIZER
|
||||
|
||||
---------------------------
|
||||
Thread-Safe C Runtime Library (macros)
|
||||
---------------------------
|
||||
strtok_r
|
||||
asctime_r
|
||||
ctime_r
|
||||
gmtime_r
|
||||
localtime_r
|
||||
rand_r
|
||||
|
||||
|
||||
The following functions are not implemented:
|
||||
|
||||
---------------------------
|
||||
RealTime Scheduling
|
||||
---------------------------
|
||||
pthread_mutex_getprioceiling
|
||||
pthread_mutex_setprioceiling
|
||||
pthread_mutex_attr_getprioceiling
|
||||
pthread_mutex_attr_getprotocol
|
||||
pthread_mutex_attr_setprioceiling
|
||||
pthread_mutex_attr_setprotocol
|
||||
|
||||
---------------------------
|
||||
Fork Handlers
|
||||
---------------------------
|
||||
pthread_atfork
|
||||
|
||||
---------------------------
|
||||
Stdio
|
||||
---------------------------
|
||||
flockfile
|
||||
ftrylockfile
|
||||
funlockfile
|
||||
getc_unlocked
|
||||
getchar_unlocked
|
||||
putc_unlocked
|
||||
putchar_unlocked
|
||||
|
||||
---------------------------
|
||||
Thread-Safe C Runtime Library
|
||||
---------------------------
|
||||
readdir_r
|
||||
getgrgid_r
|
||||
getgrnam_r
|
||||
getpwuid_r
|
||||
getpwnam_r
|
||||
|
||||
---------------------------
|
||||
Signals
|
||||
---------------------------
|
||||
sigtimedwait
|
||||
sigwait
|
||||
sigwaitinfo
|
||||
|
||||
---------------------------
|
||||
General
|
||||
---------------------------
|
||||
sysconf
|
||||
|
||||
The library includes two non-API functions for creating cancellation
|
||||
points in applications and libraries:
|
||||
|
||||
pthreadCancelableWait
|
||||
pthreadCancelableTimedWait
|
||||
|
||||
|
||||
Availability
|
||||
------------
|
||||
|
||||
The prebuilt DLL, export libs (for both MSVC and Mingw32), and the header
|
||||
files (pthread.h, semaphore.h, sched.h) are available along with the
|
||||
complete source code.
|
||||
|
||||
The source code can be found at:
|
||||
|
||||
ftp://sources.redhat.com/pub/pthreads-win32
|
||||
|
||||
and as individual source code files at
|
||||
|
||||
ftp://sources.redhat.com/pub/pthreads-win32/source
|
||||
|
||||
The pre-built DLL, export libraries and include files can be found at:
|
||||
|
||||
ftp://sources.redhat.com/pub/pthreads-win32/dll-latest
|
||||
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
There is a mailing list for discussing pthreads on Win32. To join,
|
||||
send email to:
|
||||
|
||||
[email protected]
|
||||
|
||||
|
||||
Application Development Environments
|
||||
------------------------------------
|
||||
|
||||
See the README file for more information.
|
||||
|
||||
MSVC:
|
||||
MSVC using SEH works. Distribute pthreadVSE.dll with your application.
|
||||
MSVC using C++ EH works. Distribute pthreadVCE.dll with your application.
|
||||
MSVC using C setjmp/longjmp works. Distribute pthreadVC.dll with your application.
|
||||
|
||||
|
||||
Mingw32:
|
||||
See the FAQ, Questions 6 and 10.
|
||||
|
||||
Mingw using C++ EH works. Distribute pthreadGCE.dll with your application.
|
||||
Mingw using C setjmp/longjmp works. Distribute pthreadGC.dll with your application.
|
||||
|
||||
|
||||
Cygwin: (http://sourceware.cygnus.com/cygwin/)
|
||||
Developers using Cygwin will not need pthreads-win32 since it has POSIX threads
|
||||
support. Refer to its documentation for details and extent.
|
||||
|
||||
|
||||
UWIN:
|
||||
UWIN is a complete Unix-like environment for Windows from AT&T. Pthreads-win32
|
||||
doesn't currently support UWIN (and vice versa), but that may change in the
|
||||
future.
|
||||
|
||||
Generally:
|
||||
For convenience, the following pre-built files are available on the FTP site
|
||||
(see Availability above):
|
||||
|
||||
pthread.h - for POSIX 1c threads
|
||||
semaphore.h - for POSIX 1b semaphores
|
||||
sched.h - for POSIX 1b scheduling
|
||||
pthreadVCE.dll - built with MSVC++ compiler using C++ EH
|
||||
pthreadVCE.lib
|
||||
pthreadVC.dll - built with MSVC compiler using C setjmp/longjmp
|
||||
pthreadVC.lib
|
||||
pthreadVSE.dll - built with MSVC compiler using SEH
|
||||
pthreadVSE.lib
|
||||
pthreadGCE.dll - built with Mingw32 G++ 2.95.2-1
|
||||
pthreadGC.dll - built with Mingw32 GCC 2.95.2-1 using setjmp/longjmp
|
||||
libpthreadGCE.a - derived from pthreadGCE.dll
|
||||
libpthreadGC.a - derived from pthreadGC.dll
|
||||
gcc.dll - needed if distributing applications that use
|
||||
pthreadGCE.dll (but see the FAQ Q 10 for the latest
|
||||
related information)
|
||||
|
||||
These are the only files you need in order to build POSIX threads
|
||||
applications for Win32 using either MSVC or Mingw32.
|
||||
|
||||
See the FAQ file in the source tree for additional information.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
For the authoritative reference, see the online POSIX
|
||||
standard reference at:
|
||||
|
||||
http://www.OpenGroup.org
|
||||
|
||||
For POSIX Thread API programming, several reference books are
|
||||
available:
|
||||
|
||||
Programming with POSIX Threads
|
||||
David R. Butenhof
|
||||
Addison-Wesley (pub)
|
||||
|
||||
Pthreads Programming
|
||||
By Bradford Nichols, Dick Buttlar & Jacqueline Proulx Farrell
|
||||
O'Reilly (pub)
|
||||
|
||||
On the web: see the links at the bottom of the pthreads-win32 site:
|
||||
|
||||
http://sources.redhat.com/pthreads-win32/
|
||||
|
||||
Currently, there is no documentation included in the package apart
|
||||
from the copious comments in the source code.
|
||||
|
||||
|
||||
|
||||
Enjoy!
|
||||
|
||||
Ross Johnson
|
||||
Vendored
-129
@@ -1,129 +0,0 @@
|
||||
Contributors (in approximate order of appearance)
|
||||
|
||||
[See also the ChangeLog file where individuals are
|
||||
attributed in log entries. Likewise in the FAQ file.]
|
||||
|
||||
Ben Elliston bje at cygnus dot com
|
||||
Initiated the project;
|
||||
setup the project infrastructure (CVS, web page, etc.);
|
||||
early prototype routines.
|
||||
Ross Johnson rpj at callisto dot canberra dot edu dot au
|
||||
early prototype routines;
|
||||
ongoing project coordination/maintenance;
|
||||
implementation of spin locks and barriers;
|
||||
various enhancements;
|
||||
bug fixes;
|
||||
documentation;
|
||||
testsuite.
|
||||
Robert Colquhoun rjc at trump dot net dot au
|
||||
Early bug fixes.
|
||||
John E. Bossom John dot Bossom at cognos dot com
|
||||
Contributed substantial original working implementation;
|
||||
bug fixes;
|
||||
ongoing guidance and standards interpretation.
|
||||
Anders Norlander anorland at hem2 dot passagen dot se
|
||||
Early enhancements and runtime checking for supported
|
||||
Win32 routines.
|
||||
Tor Lillqvist tml at iki dot fi
|
||||
General enhancements;
|
||||
early bug fixes to condition variables.
|
||||
Scott Lightner scott at curriculum dot com
|
||||
Bug fix.
|
||||
Kevin Ruland Kevin dot Ruland at anheuser-busch dot com
|
||||
Various bug fixes.
|
||||
Mike Russo miker at eai dot com
|
||||
Bug fix.
|
||||
Mark E. Armstrong avail at pacbell dot net
|
||||
Bug fixes.
|
||||
Lorin Hochstein lmh at xiphos dot ca
|
||||
general bug fixes; bug fixes to condition variables.
|
||||
Peter Slacik Peter dot Slacik at tatramed dot sk
|
||||
Bug fixes.
|
||||
Mumit Khan khan at xraylith dot wisc dot edu
|
||||
Fixes to work with Mingw32.
|
||||
Milan Gardian mg at tatramed dot sk
|
||||
Bug fixes and reports/analyses of obscure problems.
|
||||
Aurelio Medina aureliom at crt dot com
|
||||
First implementation of read-write locks.
|
||||
Graham Dumpleton Graham dot Dumpleton at ra dot pad dot otc dot telstra dot com dot au
|
||||
Bug fix in condition variables.
|
||||
Tristan Savatier tristan at mpegtv dot com
|
||||
WinCE port.
|
||||
Erik Hensema erik at hensema dot xs4all dot nl
|
||||
Bug fixes.
|
||||
Rich Peters rpeters at micro-magic dot com
|
||||
Todd Owen towen at lucidcalm dot dropbear dot id dot au
|
||||
Bug fixes to dll loading.
|
||||
Jason Nye jnye at nbnet dot nb dot ca
|
||||
Implementation of async cancelation.
|
||||
Fred Forester fforest at eticomm dot net
|
||||
Kevin D. Clark kclark at cabletron dot com
|
||||
David Baggett dmb at itasoftware dot com
|
||||
Bug fixes.
|
||||
Paul Redondo paul at matchvision dot com
|
||||
Scott McCaskill scott at 3dfx dot com
|
||||
Bug fixes.
|
||||
Jef Gearhart jgearhart at tpssys dot com
|
||||
Bug fix.
|
||||
Arthur Kantor akantor at bexusa dot com
|
||||
Mutex enhancements.
|
||||
Steven Reddie smr at essemer dot com dot au
|
||||
Bug fix.
|
||||
Alexander Terekhov TEREKHOV at de dot ibm dot com
|
||||
Re-implemented and improved read-write locks;
|
||||
(with Louis Thomas) re-implemented and improved
|
||||
condition variables;
|
||||
enhancements to semaphores;
|
||||
enhancements to mutexes;
|
||||
new mutex implementation in 'futex' style;
|
||||
suggested a robust implementation of pthread_once
|
||||
similar to that implemented by V.Kliathcko;
|
||||
system clock change handling re CV timeouts;
|
||||
bug fixes.
|
||||
Thomas Pfaff tpfaff at gmx dot net
|
||||
Changes to make C version usable with C++ applications;
|
||||
re-implemented mutex routines to avoid Win32 mutexes
|
||||
and TryEnterCriticalSection;
|
||||
procedure to fix Mingw32 thread-safety issues.
|
||||
Franco Bez franco dot bez at gmx dot de
|
||||
procedure to fix Mingw32 thread-safety issues.
|
||||
Louis Thomas lthomas at arbitrade dot com
|
||||
(with Alexander Terekhov) re-implemented and improved
|
||||
condition variables.
|
||||
David Korn dgk at research dot att dot com
|
||||
Ported to UWIN.
|
||||
Phil Frisbie, Jr. phil at hawksoft dot com
|
||||
Bug fix.
|
||||
Ralf Brese Ralf dot Brese at pdb4 dot siemens dot de
|
||||
Bug fix.
|
||||
prionx at juno dot com prionx at juno dot com
|
||||
Bug fixes.
|
||||
Max Woodbury mtew at cds dot duke dot edu
|
||||
POSIX versioning conditionals;
|
||||
reduced namespace pollution;
|
||||
idea to separate routines to reduce statically
|
||||
linked image sizes.
|
||||
Rob Fanner rfanner at stonethree dot com
|
||||
Bug fix.
|
||||
Michael Johnson michaelj at maine dot rr dot com
|
||||
Bug fix.
|
||||
Nicolas Barry boozai at yahoo dot com
|
||||
Bug fixes.
|
||||
Piet van Bruggen pietvb at newbridges dot nl
|
||||
Bug fix.
|
||||
Makoto Kato raven at oldskool dot jp
|
||||
AMD64 port.
|
||||
Panagiotis E. Hadjidoukas peh at hpclab dot ceid dot upatras dot gr
|
||||
Contributed the QueueUserAPCEx package which
|
||||
makes preemptive async cancelation possible.
|
||||
Will Bryant will dot bryant at ecosm dot com
|
||||
Borland compiler patch and makefile.
|
||||
Anuj Goyal anuj dot goyal at gmail dot com
|
||||
Port to Digital Mars compiler.
|
||||
Gottlob Frege gottlobfrege at gmail dot com
|
||||
re-implemented pthread_once (version 2)
|
||||
(pthread_once cancellation added by rpj).
|
||||
Vladimir Kliatchko vladimir at kliatchko dot com
|
||||
reimplemented pthread_once with the same form
|
||||
as described by A.Terekhov (later version 2);
|
||||
implementation of MCS (Mellor-Crummey/Scott) locks.
|
||||
Vendored
-150
@@ -1,150 +0,0 @@
|
||||
pthreads-win32 - a POSIX threads library for Microsoft Windows
|
||||
|
||||
|
||||
This file is Copyrighted
|
||||
------------------------
|
||||
|
||||
This file is covered under the following Copyright:
|
||||
|
||||
Copyright (C) 2001,2006 Ross P. Johnson
|
||||
All rights reserved.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Pthreads-win32 is covered by the GNU Lesser General Public License
|
||||
------------------------------------------------------------------
|
||||
|
||||
Pthreads-win32 is open software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License
|
||||
as published by the Free Software Foundation version 2.1 of the
|
||||
License.
|
||||
|
||||
Pthreads-win32 is several binary link libraries, several modules,
|
||||
associated interface definition files and scripts used to control
|
||||
its compilation and installation.
|
||||
|
||||
Pthreads-win32 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
A copy of the GNU Lesser General Public License is distributed with
|
||||
pthreads-win32 under the filename:
|
||||
|
||||
COPYING.LIB
|
||||
|
||||
You should have received a copy of the version 2.1 GNU Lesser General
|
||||
Public License with pthreads-win32; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place
|
||||
Suite 330
|
||||
Boston, MA 02111-1307
|
||||
USA
|
||||
|
||||
The contact addresses for pthreads-win32 is as follows:
|
||||
|
||||
Web: http://sources.redhat.com/pthreads-win32
|
||||
Email: Ross Johnson
|
||||
Please use: [email protected]
|
||||
|
||||
|
||||
|
||||
Pthreads-win32 copyrights and exception files
|
||||
---------------------------------------------
|
||||
|
||||
With the exception of the files listed below, Pthreads-win32
|
||||
is covered under the following GNU Lesser General Public License
|
||||
Copyrights:
|
||||
|
||||
Pthreads-win32 - POSIX Threads Library for Win32
|
||||
Copyright(C) 1998 John E. Bossom
|
||||
Copyright(C) 1999,2006 Pthreads-win32 contributors
|
||||
|
||||
The current list of contributors is contained
|
||||
in the file CONTRIBUTORS included with the source
|
||||
code distribution. The current list of CONTRIBUTORS
|
||||
can also be seen at the following WWW location:
|
||||
http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
|
||||
Contact Email: Ross Johnson
|
||||
Please use: [email protected]
|
||||
|
||||
These files are not covered under one of the Copyrights listed above:
|
||||
|
||||
COPYING
|
||||
COPYING.LIB
|
||||
tests/rwlock7.c
|
||||
|
||||
This file, COPYING, is distributed under the Copyright found at the
|
||||
top of this file. It is important to note that you may distribute
|
||||
verbatim copies of this file but you may not modify this file.
|
||||
|
||||
The file COPYING.LIB, which contains a copy of the version 2.1
|
||||
GNU Lesser General Public License, is itself copyrighted by the
|
||||
Free Software Foundation, Inc. Please note that the Free Software
|
||||
Foundation, Inc. does NOT have a copyright over Pthreads-win32,
|
||||
only the COPYING.LIB that is supplied with pthreads-win32.
|
||||
|
||||
The file tests/rwlock7.c is derived from code written by
|
||||
Dave Butenhof for his book 'Programming With POSIX(R) Threads'.
|
||||
The original code was obtained by free download from his website
|
||||
http://home.earthlink.net/~anneart/family/Threads/source.html
|
||||
and did not contain a copyright or author notice. It is assumed to
|
||||
be freely distributable.
|
||||
|
||||
In all cases one may use and distribute these exception files freely.
|
||||
And because one may freely distribute the LGPL covered files, the
|
||||
entire pthreads-win32 source may be freely used and distributed.
|
||||
|
||||
|
||||
|
||||
General Copyleft and License info
|
||||
---------------------------------
|
||||
|
||||
For general information on Copylefts, see:
|
||||
|
||||
http://www.gnu.org/copyleft/
|
||||
|
||||
For information on GNU Lesser General Public Licenses, see:
|
||||
|
||||
http://www.gnu.org/copyleft/lesser.html
|
||||
http://www.gnu.org/copyleft/lesser.txt
|
||||
|
||||
|
||||
Why pthreads-win32 did not use the GNU General Public License
|
||||
-------------------------------------------------------------
|
||||
|
||||
The goal of the pthreads-win32 project has been to
|
||||
provide a quality and complete implementation of the POSIX
|
||||
threads API for Microsoft Windows within the limits imposed
|
||||
by virtue of it being a stand-alone library and not
|
||||
linked directly to other POSIX compliant libraries. For
|
||||
example, some functions and features, such as those based
|
||||
on POSIX signals, are missing.
|
||||
|
||||
Pthreads-win32 is a library, available in several different
|
||||
versions depending on supported compilers, and may be used
|
||||
as a dynamically linked module or a statically linked set of
|
||||
binary modules. It is not an application on it's own.
|
||||
|
||||
It was fully intended that pthreads-win32 be usable with
|
||||
commercial software not covered by either the GPL or the LGPL
|
||||
licenses. Pthreads-win32 has many contributors to it's
|
||||
code base, many of whom have done so because they have
|
||||
used the library in commercial or proprietry software
|
||||
projects.
|
||||
|
||||
Releasing pthreads-win32 under the LGPL ensures that the
|
||||
library can be used widely, while at the same time ensures
|
||||
that bug fixes and improvements to the pthreads-win32 code
|
||||
itself is returned to benefit all current and future users
|
||||
of the library.
|
||||
|
||||
Although pthreads-win32 makes it possible for applications
|
||||
that use POSIX threads to be ported to Win32 platforms, the
|
||||
broader goal of the project is to encourage the use of open
|
||||
standards, and in particular, to make it just a little easier
|
||||
for developers writing Win32 applications to consider
|
||||
widening the potential market for their products.
|
||||
Vendored
-504
@@ -1,504 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
Vendored
-516
@@ -1,516 +0,0 @@
|
||||
/*
|
||||
* implement.h
|
||||
*
|
||||
* Definitions that don't need to be public.
|
||||
*
|
||||
* Keeps all the internals out of pthread.h
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _IMPLEMENT_H
|
||||
#define _IMPLEMENT_H
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
/* use local include files during development */
|
||||
#include "semaphore.h"
|
||||
#include "sched.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/*
|
||||
* This enumeration represents the state of the thread;
|
||||
* The thread is still "alive" if the numeric value of the
|
||||
* state is greater or equal "PThreadStateRunning".
|
||||
*/
|
||||
PThreadStateInitial = 0, /* Thread not running */
|
||||
PThreadStateRunning, /* Thread alive & kicking */
|
||||
PThreadStateSuspended, /* Thread alive but suspended */
|
||||
PThreadStateCancelPending, /* Thread alive but is */
|
||||
/* has cancelation pending. */
|
||||
PThreadStateCanceling, /* Thread alive but is */
|
||||
/* in the process of terminating */
|
||||
/* due to a cancellation request */
|
||||
PThreadStateException, /* Thread alive but exiting */
|
||||
/* due to an exception */
|
||||
PThreadStateLast
|
||||
}
|
||||
PThreadState;
|
||||
|
||||
|
||||
typedef struct pte_thread_t_ pte_thread_t;
|
||||
|
||||
struct pte_thread_t_
|
||||
{
|
||||
pte_osThreadHandle threadId; /* OS specific thread handle */
|
||||
// pthread_t ptHandle; /* This thread's permanent pthread_t handle */
|
||||
pte_thread_t * prevReuse; /* Links threads on reuse stack */
|
||||
volatile PThreadState state;
|
||||
void *exitStatus;
|
||||
void *parms;
|
||||
int ptErrno;
|
||||
int detachState;
|
||||
pthread_mutex_t threadLock; /* Used for serialised access to public thread state */
|
||||
int sched_priority; /* As set, not as currently is */
|
||||
pthread_mutex_t cancelLock; /* Used for async-cancel safety */
|
||||
int cancelState;
|
||||
int cancelType;
|
||||
int cancelEvent;
|
||||
jmp_buf start_mark;
|
||||
int implicit:
|
||||
1;
|
||||
void *keys;
|
||||
void *nextAssoc;
|
||||
|
||||
unsigned int x; /* Extra information - reuse count etc */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Special value to mark attribute objects as valid.
|
||||
*/
|
||||
#define PTE_ATTR_VALID ((unsigned long) 0xC4C0FFEE)
|
||||
|
||||
struct pthread_attr_t_
|
||||
{
|
||||
unsigned long valid;
|
||||
void *stackaddr;
|
||||
size_t stacksize;
|
||||
int detachstate;
|
||||
struct sched_param param;
|
||||
int inheritsched;
|
||||
int contentionscope;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Semaphores, Mutexes and Condition Variables
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
|
||||
struct sem_t_
|
||||
{
|
||||
int value;
|
||||
pthread_mutex_t lock;
|
||||
pte_osSemaphoreHandle sem;
|
||||
};
|
||||
|
||||
#define PTE_OBJECT_AUTO_INIT ((void *) -1)
|
||||
#define PTE_OBJECT_INVALID 0
|
||||
|
||||
struct pthread_mutex_t_
|
||||
{
|
||||
pte_osSemaphoreHandle handle;
|
||||
int lock_idx;
|
||||
/* Provides exclusive access to mutex state
|
||||
via the Interlocked* mechanism.
|
||||
0: unlocked/free.
|
||||
1: locked - no other waiters.
|
||||
-1: locked - with possible other waiters.
|
||||
*/
|
||||
int recursive_count; /* Number of unlocks a thread needs to perform
|
||||
before the lock is released (recursive
|
||||
mutexes only). */
|
||||
int kind; /* Mutex type. */
|
||||
pthread_t ownerThread;
|
||||
};
|
||||
|
||||
struct pthread_mutexattr_t_
|
||||
{
|
||||
int pshared;
|
||||
int kind;
|
||||
};
|
||||
|
||||
/*
|
||||
* Possible values, other than PTE_OBJECT_INVALID,
|
||||
* for the "interlock" element in a spinlock.
|
||||
*
|
||||
* In this implementation, when a spinlock is initialised,
|
||||
* the number of cpus available to the process is checked.
|
||||
* If there is only one cpu then "interlock" is set equal to
|
||||
* PTE_SPIN_USE_MUTEX and u.mutex is a initialised mutex.
|
||||
* If the number of cpus is greater than 1 then "interlock"
|
||||
* is set equal to PTE_SPIN_UNLOCKED and the number is
|
||||
* stored in u.cpus. This arrangement allows the spinlock
|
||||
* routines to attempt an InterlockedCompareExchange on "interlock"
|
||||
* immediately and, if that fails, to try the inferior mutex.
|
||||
*
|
||||
* "u.cpus" isn't used for anything yet, but could be used at
|
||||
* some point to optimise spinlock behaviour.
|
||||
*/
|
||||
#define PTE_SPIN_UNLOCKED (1)
|
||||
#define PTE_SPIN_LOCKED (2)
|
||||
#define PTE_SPIN_USE_MUTEX (3)
|
||||
|
||||
struct pthread_spinlock_t_
|
||||
{
|
||||
int interlock; /* Locking element for multi-cpus. */
|
||||
union
|
||||
{
|
||||
int cpus; /* No. of cpus if multi cpus, or */
|
||||
pthread_mutex_t mutex; /* mutex if single cpu. */
|
||||
} u;
|
||||
};
|
||||
|
||||
struct pthread_barrier_t_
|
||||
{
|
||||
unsigned int nCurrentBarrierHeight;
|
||||
unsigned int nInitialBarrierHeight;
|
||||
int iStep;
|
||||
int pshared;
|
||||
sem_t semBarrierBreeched[2];
|
||||
};
|
||||
|
||||
struct pthread_barrierattr_t_
|
||||
{
|
||||
int pshared;
|
||||
};
|
||||
|
||||
struct pthread_key_t_
|
||||
{
|
||||
unsigned key;
|
||||
void (*destructor) (void *);
|
||||
pthread_mutex_t keyLock;
|
||||
void *threads;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ThreadParms ThreadParms;
|
||||
typedef struct ThreadKeyAssoc ThreadKeyAssoc;
|
||||
|
||||
struct ThreadParms
|
||||
{
|
||||
pthread_t tid;
|
||||
void *(*start) (void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct pthread_cond_t_
|
||||
{
|
||||
long nWaitersBlocked; /* Number of threads blocked */
|
||||
long nWaitersGone; /* Number of threads timed out */
|
||||
long nWaitersToUnblock; /* Number of threads to unblock */
|
||||
sem_t semBlockQueue; /* Queue up threads waiting for the */
|
||||
/* condition to become signalled */
|
||||
sem_t semBlockLock; /* Semaphore that guards access to */
|
||||
/* | waiters blocked count/block queue */
|
||||
/* +-> Mandatory Sync.LEVEL-1 */
|
||||
pthread_mutex_t mtxUnblockLock; /* Mutex that guards access to */
|
||||
/* | waiters (to)unblock(ed) counts */
|
||||
/* +-> Optional* Sync.LEVEL-2 */
|
||||
pthread_cond_t next; /* Doubly linked list */
|
||||
pthread_cond_t prev;
|
||||
};
|
||||
|
||||
|
||||
struct pthread_condattr_t_
|
||||
{
|
||||
int pshared;
|
||||
};
|
||||
|
||||
#define PTE_RWLOCK_MAGIC 0xfacade2
|
||||
|
||||
struct pthread_rwlock_t_
|
||||
{
|
||||
pthread_mutex_t mtxExclusiveAccess;
|
||||
pthread_mutex_t mtxSharedAccessCompleted;
|
||||
pthread_cond_t cndSharedAccessCompleted;
|
||||
int nSharedAccessCount;
|
||||
int nExclusiveAccessCount;
|
||||
int nCompletedSharedAccessCount;
|
||||
int nMagic;
|
||||
};
|
||||
|
||||
struct pthread_rwlockattr_t_
|
||||
{
|
||||
int pshared;
|
||||
};
|
||||
|
||||
/*
|
||||
* MCS lock queue node - see pte_MCS_lock.c
|
||||
*/
|
||||
struct pte_mcs_node_t_
|
||||
{
|
||||
struct pte_mcs_node_t_ **lock; /* ptr to tail of queue */
|
||||
struct pte_mcs_node_t_ *next; /* ptr to successor in queue */
|
||||
unsigned int readyFlag; /* set after lock is released by
|
||||
predecessor */
|
||||
unsigned int nextFlag; /* set after 'next' ptr is set by
|
||||
successor */
|
||||
};
|
||||
|
||||
typedef struct pte_mcs_node_t_ pte_mcs_local_node_t;
|
||||
typedef struct pte_mcs_node_t_ *pte_mcs_lock_t;
|
||||
|
||||
|
||||
struct ThreadKeyAssoc
|
||||
{
|
||||
/*
|
||||
* Purpose:
|
||||
* This structure creates an association between a thread and a key.
|
||||
* It is used to implement the implicit invocation of a user defined
|
||||
* destroy routine for thread specific data registered by a user upon
|
||||
* exiting a thread.
|
||||
*
|
||||
* Graphically, the arrangement is as follows, where:
|
||||
*
|
||||
* K - Key with destructor
|
||||
* (head of chain is key->threads)
|
||||
* T - Thread that has called pthread_setspecific(Kn)
|
||||
* (head of chain is thread->keys)
|
||||
* A - Association. Each association is a node at the
|
||||
* intersection of two doubly-linked lists.
|
||||
*
|
||||
* T1 T2 T3
|
||||
* | | |
|
||||
* | | |
|
||||
* K1 -----+-----A-----A----->
|
||||
* | | |
|
||||
* | | |
|
||||
* K2 -----A-----A-----+----->
|
||||
* | | |
|
||||
* | | |
|
||||
* K3 -----A-----+-----A----->
|
||||
* | | |
|
||||
* | | |
|
||||
* V V V
|
||||
*
|
||||
* Access to the association is guarded by two locks: the key's
|
||||
* general lock (guarding the row) and the thread's general
|
||||
* lock (guarding the column). This avoids the need for a
|
||||
* dedicated lock for each association, which not only consumes
|
||||
* more handles but requires that: before the lock handle can
|
||||
* be released - both the key must be deleted and the thread
|
||||
* must have called the destructor. The two-lock arrangement
|
||||
* allows the resources to be freed as soon as either thread or
|
||||
* key is concluded.
|
||||
*
|
||||
* To avoid deadlock: whenever both locks are required, the key
|
||||
* and thread locks are always acquired in the order: key lock
|
||||
* then thread lock. An exception to this exists when a thread
|
||||
* calls the destructors, however this is done carefully to
|
||||
* avoid deadlock.
|
||||
*
|
||||
* An association is created when a thread first calls
|
||||
* pthread_setspecific() on a key that has a specified
|
||||
* destructor.
|
||||
*
|
||||
* An association is destroyed either immediately after the
|
||||
* thread calls the key destructor function on thread exit, or
|
||||
* when the key is deleted.
|
||||
*
|
||||
* Attributes:
|
||||
* thread
|
||||
* reference to the thread that owns the
|
||||
* association. This is actually the pointer to the
|
||||
* thread struct itself. Since the association is
|
||||
* destroyed before the thread exits, this can never
|
||||
* point to a different logical thread to the one that
|
||||
* created the assoc, i.e. after thread struct reuse.
|
||||
*
|
||||
* key
|
||||
* reference to the key that owns the association.
|
||||
*
|
||||
* nextKey
|
||||
* The pthread_t->keys attribute is the head of a
|
||||
* chain of associations that runs through the nextKey
|
||||
* link. This chain provides the 1 to many relationship
|
||||
* between a pthread_t and all pthread_key_t on which
|
||||
* it called pthread_setspecific.
|
||||
*
|
||||
* prevKey
|
||||
* Similarly.
|
||||
*
|
||||
* nextThread
|
||||
* The pthread_key_t->threads attribute is the head of
|
||||
* a chain of assoctiations that runs through the
|
||||
* nextThreads link. This chain provides the 1 to many
|
||||
* relationship between a pthread_key_t and all the
|
||||
* PThreads that have called pthread_setspecific for
|
||||
* this pthread_key_t.
|
||||
*
|
||||
* prevThread
|
||||
* Similarly.
|
||||
*
|
||||
* Notes:
|
||||
* 1) As soon as either the key or the thread is no longer
|
||||
* referencing the association, it can be destroyed. The
|
||||
* association will be removed from both chains.
|
||||
*
|
||||
* 2) An association is only created by
|
||||
* pthread_setspecific if the user provided a
|
||||
* destroyRoutine when they created the key.
|
||||
*
|
||||
*
|
||||
*/
|
||||
pte_thread_t * thread;
|
||||
pthread_key_t key;
|
||||
ThreadKeyAssoc *nextKey;
|
||||
ThreadKeyAssoc *nextThread;
|
||||
ThreadKeyAssoc *prevKey;
|
||||
ThreadKeyAssoc *prevThread;
|
||||
};
|
||||
|
||||
/*
|
||||
* Services available through EXCEPTION_PTE_SERVICES
|
||||
* and also used [as parameters to pte_throw()] as
|
||||
* generic exception selectors.
|
||||
*/
|
||||
|
||||
#define PTE_EPS_EXIT (1)
|
||||
#define PTE_EPS_CANCEL (2)
|
||||
|
||||
|
||||
/* Useful macros */
|
||||
#define PTE_MAX(a,b) ((a)<(b)?(b):(a))
|
||||
#define PTE_MIN(a,b) ((a)>(b)?(b):(a))
|
||||
|
||||
|
||||
/* Thread Reuse stack bottom marker. Must not be NULL or any valid pointer to memory. */
|
||||
#define PTE_THREAD_REUSE_EMPTY ((pte_thread_t *) 1)
|
||||
|
||||
extern int pte_processInitialized;
|
||||
extern pte_thread_t * pte_threadReuseTop;
|
||||
extern pte_thread_t * pte_threadReuseBottom;
|
||||
extern pthread_key_t pte_selfThreadKey;
|
||||
extern pthread_key_t pte_cleanupKey;
|
||||
extern pthread_cond_t pte_cond_list_head;
|
||||
extern pthread_cond_t pte_cond_list_tail;
|
||||
|
||||
extern int pte_mutex_default_kind;
|
||||
|
||||
extern int pte_concurrency;
|
||||
|
||||
extern int pte_features;
|
||||
|
||||
extern pte_osMutexHandle pte_thread_reuse_lock;
|
||||
extern pte_osMutexHandle pte_mutex_test_init_lock;
|
||||
extern pte_osMutexHandle pte_cond_list_lock;
|
||||
extern pte_osMutexHandle pte_cond_test_init_lock;
|
||||
extern pte_osMutexHandle pte_rwlock_test_init_lock;
|
||||
extern pte_osMutexHandle pte_spinlock_test_init_lock;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* =====================
|
||||
* =====================
|
||||
* Forward Declarations
|
||||
* =====================
|
||||
* =====================
|
||||
*/
|
||||
|
||||
int pte_is_attr (const pthread_attr_t * attr);
|
||||
|
||||
int pte_cond_check_need_init (pthread_cond_t * cond);
|
||||
int pte_mutex_check_need_init (pthread_mutex_t * mutex);
|
||||
int pte_rwlock_check_need_init (pthread_rwlock_t * rwlock);
|
||||
int pte_spinlock_check_need_init (pthread_spinlock_t * lock);
|
||||
|
||||
int pte_processInitialize (void);
|
||||
|
||||
void pte_processTerminate (void);
|
||||
|
||||
void pte_threadDestroy (pthread_t tid);
|
||||
void pte_threadExitAndDestroy (pthread_t tid);
|
||||
|
||||
void pte_pop_cleanup_all (int execute);
|
||||
|
||||
pthread_t pte_new (void);
|
||||
|
||||
pthread_t pte_threadReusePop (void);
|
||||
|
||||
void pte_threadReusePush (pthread_t thread);
|
||||
|
||||
int pte_getprocessors (int *count);
|
||||
|
||||
int pte_setthreadpriority (pthread_t thread, int policy, int priority);
|
||||
|
||||
void pte_rwlock_cancelwrwait (void *arg);
|
||||
|
||||
int pte_threadStart (void *vthreadParms);
|
||||
|
||||
void pte_callUserDestroyRoutines (pthread_t thread);
|
||||
|
||||
int pte_tkAssocCreate (pte_thread_t * thread, pthread_key_t key);
|
||||
|
||||
void pte_tkAssocDestroy (ThreadKeyAssoc * assoc);
|
||||
|
||||
int sem_wait_nocancel (sem_t * sem);
|
||||
|
||||
unsigned int pte_relmillisecs (const struct timespec * abstime);
|
||||
|
||||
void pte_mcs_lock_acquire (pte_mcs_lock_t * lock, pte_mcs_local_node_t * node);
|
||||
|
||||
void pte_mcs_lock_release (pte_mcs_local_node_t * node);
|
||||
|
||||
/* Declared in private.c */
|
||||
void pte_throw (unsigned int exception);
|
||||
|
||||
int pte_cancellable_wait (pte_osSemaphoreHandle semHandle, unsigned int* timeout);
|
||||
|
||||
#define PTE_ATOMIC_EXCHANGE pte_osAtomicExchange
|
||||
#define PTE_ATOMIC_EXCHANGE_ADD pte_osAtomicExchangeAdd
|
||||
#define PTE_ATOMIC_COMPARE_EXCHANGE pte_osAtomicCompareExchange
|
||||
#define PTE_ATOMIC_DECREMENT pte_osAtomicDecrement
|
||||
#define PTE_ATOMIC_INCREMENT pte_osAtomicIncrement
|
||||
|
||||
int pte_thread_detach_np();
|
||||
int pte_thread_detach_and_exit_np();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* _IMPLEMENT_H */
|
||||
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
/***
|
||||
* errno.h - system wide error numbers (set by system calls)
|
||||
*
|
||||
* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Purpose:
|
||||
* This file defines the system-wide error numbers (set by
|
||||
* system calls). Conforms to the XENIX standard. Extended
|
||||
* for compatibility with Uniforum standard.
|
||||
* [System V]
|
||||
*
|
||||
* [Public]
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef _INC_ERRNO
|
||||
#define _INC_ERRNO
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* declare reference to errno */
|
||||
|
||||
extern int errno;
|
||||
|
||||
/* Error Codes */
|
||||
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define EIO 5
|
||||
#define ENXIO 6
|
||||
#define E2BIG 7
|
||||
#define ENOEXEC 8
|
||||
#define EBADF 9
|
||||
#define ECHILD 10
|
||||
#define EAGAIN 11
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EFAULT 14
|
||||
#define EBUSY 16
|
||||
#define EEXIST 17
|
||||
#define EXDEV 18
|
||||
#define ENODEV 19
|
||||
#define ENOTDIR 20
|
||||
#define EISDIR 21
|
||||
#define EINVAL 22
|
||||
#define ENFILE 23
|
||||
#define EMFILE 24
|
||||
#define ENOTTY 25
|
||||
#define EFBIG 27
|
||||
#define ENOSPC 28
|
||||
#define ESPIPE 29
|
||||
#define EROFS 30
|
||||
#define EMLINK 31
|
||||
#define EPIPE 32
|
||||
#define EDOM 33
|
||||
#define ERANGE 34
|
||||
#define EDEADLK 36
|
||||
|
||||
/* defined differently in winsock.h on WinCE */
|
||||
#ifndef ENAMETOOLONG
|
||||
#define ENAMETOOLONG 38
|
||||
#endif
|
||||
|
||||
#define ENOLCK 39
|
||||
#define ENOSYS 40
|
||||
|
||||
/* defined differently in winsock.h on WinCE */
|
||||
#ifndef ENOTEMPTY
|
||||
#define ENOTEMPTY 41
|
||||
#endif
|
||||
|
||||
#define EILSEQ 42
|
||||
|
||||
/*
|
||||
* Support EDEADLOCK for compatibiity with older MS-C versions.
|
||||
*/
|
||||
#define EDEADLOCK EDEADLK
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _INC_ERRNO */
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* tls-helper.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tls-helper.h"
|
||||
|
||||
static int *keysUsed;
|
||||
|
||||
/* We don't protect this - it's only written on startup */
|
||||
static int maxTlsValues;
|
||||
|
||||
pte_osMutexHandle globalTlsLock;
|
||||
|
||||
pte_osResult pteTlsGlobalInit(int maxEntries)
|
||||
{
|
||||
int i;
|
||||
pte_osResult result;
|
||||
|
||||
pte_osMutexCreate(&globalTlsLock);
|
||||
|
||||
keysUsed = (int *) malloc(maxEntries * sizeof(int));
|
||||
|
||||
if (keysUsed != NULL)
|
||||
{
|
||||
for (i=0;i<maxEntries;i++)
|
||||
keysUsed[i] = 0;
|
||||
|
||||
maxTlsValues = maxEntries;
|
||||
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
else
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void * pteTlsThreadInit(void)
|
||||
{
|
||||
int i;
|
||||
void **pTlsStruct = (void **) malloc(maxTlsValues * sizeof(void*));
|
||||
|
||||
/* PTE library assumes that keys are initialized to zero */
|
||||
for (i=0; i<maxTlsValues;i++)
|
||||
pTlsStruct[i] = 0;
|
||||
|
||||
return (void *) pTlsStruct;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pteTlsAlloc(unsigned int *pKey)
|
||||
{
|
||||
int i;
|
||||
pte_osResult result = PTE_OS_NO_RESOURCES;
|
||||
|
||||
pte_osMutexLock(globalTlsLock);
|
||||
|
||||
for (i=0;i<maxTlsValues;i++)
|
||||
{
|
||||
if (keysUsed[i] == 0)
|
||||
{
|
||||
keysUsed[i] = 1;
|
||||
|
||||
*pKey = i+1;
|
||||
result = PTE_OS_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(globalTlsLock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void * pteTlsGetValue(void *pTlsThreadStruct, unsigned int index)
|
||||
{
|
||||
void **pTls = (void **) pTlsThreadStruct;
|
||||
|
||||
if (keysUsed[index-1])
|
||||
{
|
||||
if (pTls != NULL)
|
||||
return pTls[index-1];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void * value)
|
||||
{
|
||||
pte_osResult result;
|
||||
void ** pTls = (void **) pTlsThreadStruct;
|
||||
|
||||
if (pTls != NULL)
|
||||
{
|
||||
pTls[index-1] = value;
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
else
|
||||
result = PTE_OS_INVALID_PARAM;
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
pte_osResult pteTlsFree(unsigned int index)
|
||||
{
|
||||
pte_osResult result;
|
||||
|
||||
if (keysUsed != NULL)
|
||||
{
|
||||
pte_osMutexLock(globalTlsLock);
|
||||
|
||||
keysUsed[index-1] = 0;
|
||||
|
||||
pte_osMutexUnlock(globalTlsLock);
|
||||
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
else
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pteTlsThreadDestroy(void * pTlsThreadStruct)
|
||||
{
|
||||
free(pTlsThreadStruct);
|
||||
}
|
||||
|
||||
void pteTlsGlobalDestroy(void)
|
||||
{
|
||||
pte_osMutexDelete(globalTlsLock);
|
||||
free(keysUsed);
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* tls-helper.h
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef _TLS_HELPER_H_
|
||||
#define _TLS_HELPER_H_
|
||||
|
||||
#include "../psp/pte_osal.h"
|
||||
|
||||
/* @todo document.. */
|
||||
|
||||
pte_osResult pteTlsGlobalInit(int maxEntries);
|
||||
void * pteTlsThreadInit(void);
|
||||
|
||||
pte_osResult pteTlsAlloc(unsigned int *pKey);
|
||||
void * pteTlsGetValue(void *pTlsThreadStruct, unsigned int index);
|
||||
pte_osResult pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void * value);
|
||||
pte_osResult pteTlsFree(unsigned int index);
|
||||
|
||||
void pteTlsThreadDestroy(void * pTlsThreadStruct);
|
||||
void pteTlsGlobalDestroy(void);
|
||||
|
||||
#endif // _TLS_HELPER_H_
|
||||
Vendored
-51
@@ -1,51 +0,0 @@
|
||||
VPATH = ../..:../helper
|
||||
|
||||
TARGET_LIB = libpthread-psp.a
|
||||
|
||||
MUTEX_OBJS = pthread_mutex.o
|
||||
|
||||
MUTEXATTR_OBJS = pthread_mutexattr.o
|
||||
|
||||
SUPPORT_OBJS = pte.o pthread.o
|
||||
|
||||
THREAD_OBJS = sched.o
|
||||
|
||||
TLS_OBJS = pthread_key.o
|
||||
|
||||
MISC_OBJS = pthread_get.o
|
||||
|
||||
SEM_OBJS = sem.o
|
||||
|
||||
BARRIER_OBJS = pthread_barrier.o pthread_barrierattr.o
|
||||
|
||||
SPIN_OBJS = pthread_spin.o
|
||||
|
||||
CONDVAR_OBJS = pthread_cond.o pthread_condattr.o
|
||||
|
||||
RWLOCK_OBJS = pthread_rwlock.o pthread_rwlockattr.o
|
||||
|
||||
CANCEL_OBJS = pthread.o pthread_set.o
|
||||
|
||||
OS_OBJS = psp_osal.o tls-helper.o
|
||||
|
||||
OBJS = $(MUTEX_OBJS) $(MUTEXATTR_OBJS) $(THREAD_OBJS) $(SUPPORT_OBJS) $(TLS_OBJS) $(MISC_OBJS) $(SEM_OBJS) $(BARRIER_OBJS) $(SPIN_OBJS) $(CONDVAR_OBJS) $(RWLOCK_OBJS) $(CANCEL_OBJS) $(OS_OBJS)
|
||||
|
||||
INCDIR =
|
||||
CFLAGS = $(GLOBAL_CFLAGS) -G0 -O2 -Wall -g -fno-strict-aliasing -I../.. -I../helper
|
||||
CXXFLAGS = $(CFLAGS) -fexceptions -fno-rtti -Werror
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
LDFLAGS =
|
||||
LIBS =
|
||||
|
||||
PSPSDK=$(shell psp-config --pspsdk-path)
|
||||
include $(PSPSDK)/lib/build.mak
|
||||
|
||||
test :
|
||||
make -C ../../tests test
|
||||
|
||||
install: $(TARGET_LIB)
|
||||
@cp -v $(TARGET_LIB) `psp-config --psp-prefix`/lib
|
||||
@cp -v *.h `psp-config --psp-prefix`/include
|
||||
@echo "Done."
|
||||
|
||||
-842
@@ -1,842 +0,0 @@
|
||||
/*
|
||||
* psp_osal.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* For ftime */
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#include <pspkerror.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
#include "../../pthread.h"
|
||||
#include "../helper/tls-helper.h"
|
||||
|
||||
#define MAX_PSP_UID 2048 /* SWAG */
|
||||
|
||||
#define DEFAULT_STACK_SIZE_BYTES 4096
|
||||
|
||||
#define PSP_MAX_TLS 32
|
||||
|
||||
#if 1
|
||||
#define PSP_DEBUG(x) printf(x)
|
||||
#else
|
||||
#define PSP_DEBUG(x)
|
||||
#endif
|
||||
|
||||
/* TLS key used to access pspThreadData struct for reach thread. */
|
||||
static unsigned int threadDataKey;
|
||||
|
||||
/*
|
||||
* Data stored on a per-thread basis - allocated in pte_osThreadCreate
|
||||
* and freed in pte_osThreadDelete.
|
||||
*/
|
||||
typedef struct pspThreadData
|
||||
{
|
||||
/* Entry point and parameters to thread's main function */
|
||||
pte_osThreadEntryPoint entryPoint;
|
||||
void * argv;
|
||||
|
||||
/* Semaphore used for cancellation. Posted to by pte_osThreadCancel,
|
||||
polled in pte_osSemaphoreCancellablePend */
|
||||
SceUID cancelSem;
|
||||
|
||||
} pspThreadData;
|
||||
|
||||
|
||||
/* Structure used to emulate TLS on non-POSIX threads.
|
||||
* This limits us to one non-POSIX thread that can
|
||||
* call pthread functions. */
|
||||
static void *globalTls;
|
||||
|
||||
/* Helper functions */
|
||||
static pspThreadData *getThreadData(SceUID threadHandle);
|
||||
static void *getTlsStructFromThread(SceUID thid);
|
||||
|
||||
/* A new thread's stub entry point. It retrieves the real entry point from the per thread control
|
||||
* data as well as any parameters to this function, and then calls the entry point.
|
||||
*/
|
||||
int pspStubThreadEntry (unsigned int argc, void *argv)
|
||||
{
|
||||
int result;
|
||||
pspThreadData *pThreadData;
|
||||
|
||||
pThreadData = getThreadData(sceKernelGetThreadId());
|
||||
|
||||
result = (*(pThreadData->entryPoint))(pThreadData->argv);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Initialization
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osInit(void)
|
||||
{
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData;
|
||||
char cancelSemName[64];
|
||||
|
||||
/* Allocate and initialize TLS support */
|
||||
result = pteTlsGlobalInit(PSP_MAX_TLS);
|
||||
|
||||
if (result == PTE_OS_OK)
|
||||
{
|
||||
/* Allocate a key that we use to store control information (e.g. cancellation semaphore) per thread */
|
||||
result = pteTlsAlloc(&threadDataKey);
|
||||
|
||||
if (result == PTE_OS_OK)
|
||||
{
|
||||
|
||||
/* Initialize the structure used to emulate TLS for
|
||||
* non-POSIX threads
|
||||
*/
|
||||
globalTls = pteTlsThreadInit();
|
||||
|
||||
/* Also create a "thread data" structure for a single non-POSIX thread. */
|
||||
|
||||
/* Allocate some memory for our per-thread control data. We use this for:
|
||||
* 1. Entry point and parameters for the user thread's main function.
|
||||
* 2. Semaphore used for thread cancellation.
|
||||
*/
|
||||
pThreadData = (pspThreadData *) malloc(sizeof(pspThreadData));
|
||||
|
||||
if (pThreadData == NULL)
|
||||
{
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Save a pointer to our per-thread control data as a TLS value */
|
||||
pteTlsSetValue(globalTls, threadDataKey, pThreadData);
|
||||
|
||||
/* Create a semaphore used to cancel threads */
|
||||
snprintf(cancelSemName, sizeof(cancelSemName), "pthread_cancelSemGlobal");
|
||||
|
||||
pThreadData->cancelSem = sceKernelCreateSema(cancelSemName,
|
||||
0, /* attributes (default) */
|
||||
0, /* initial value */
|
||||
255, /* maximum value */
|
||||
0); /* options (default) */
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Threads
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint,
|
||||
int stackSize,
|
||||
int initialPriority,
|
||||
void *argv,
|
||||
pte_osThreadHandle* ppte_osThreadHandle)
|
||||
{
|
||||
char threadName[64];
|
||||
char cancelSemName[64];
|
||||
static int threadNum = 1;
|
||||
int pspAttr;
|
||||
void *pTls;
|
||||
SceUID threadId;
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData;
|
||||
|
||||
if (threadNum++ > MAX_PSP_UID)
|
||||
threadNum = 0;
|
||||
|
||||
/* Make sure that the stack we're going to allocate is big enough */
|
||||
if (stackSize < DEFAULT_STACK_SIZE_BYTES)
|
||||
stackSize = DEFAULT_STACK_SIZE_BYTES;
|
||||
|
||||
/* Allocate TLS structure for this thread. */
|
||||
pTls = pteTlsThreadInit();
|
||||
if (pTls == NULL)
|
||||
{
|
||||
PSP_DEBUG("pteTlsThreadInit: PTE_OS_NO_RESOURCES\n");
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/* Allocate some memory for our per-thread control data. We use this for:
|
||||
* 1. Entry point and parameters for the user thread's main function.
|
||||
* 2. Semaphore used for thread cancellation.
|
||||
*/
|
||||
pThreadData = (pspThreadData *) malloc(sizeof(pspThreadData));
|
||||
|
||||
if (pThreadData == NULL)
|
||||
{
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
PSP_DEBUG("malloc(pspThreadData): PTE_OS_NO_RESOURCES\n");
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/* Save a pointer to our per-thread control data as a TLS value */
|
||||
pteTlsSetValue(pTls, threadDataKey, pThreadData);
|
||||
|
||||
pThreadData->entryPoint = entryPoint;
|
||||
pThreadData->argv = argv;
|
||||
|
||||
/* Create a semaphore used to cancel threads */
|
||||
snprintf(cancelSemName, sizeof(cancelSemName), "pthread_cancelSem%04d", threadNum);
|
||||
|
||||
pThreadData->cancelSem = sceKernelCreateSema(cancelSemName,
|
||||
0, /* attributes (default) */
|
||||
0, /* initial value */
|
||||
255, /* maximum value */
|
||||
0); /* options (default) */
|
||||
|
||||
|
||||
/* In order to emulate TLS functionality, we append the address of the TLS structure that we
|
||||
* allocated above to the thread's name. To set or get TLS values for this thread, the user
|
||||
* needs to get the name of the thread from the OS and then parse the name to extract
|
||||
* a pointer to the TLS structure.
|
||||
*/
|
||||
snprintf(threadName, sizeof(threadName), "pthread%04d__%x", threadNum, (unsigned int) pTls);
|
||||
|
||||
pspAttr = 0;
|
||||
|
||||
#if 0
|
||||
printf("%s %p %d %d %d\n",threadName, pspStubThreadEntry, initialPriority, stackSize, pspAttr);
|
||||
#endif
|
||||
threadId = sceKernelCreateThread(threadName,
|
||||
pspStubThreadEntry,
|
||||
initialPriority,
|
||||
stackSize,
|
||||
pspAttr,
|
||||
NULL);
|
||||
|
||||
if (threadId == (SceUID) SCE_KERNEL_ERROR_NO_MEMORY)
|
||||
{
|
||||
free(pThreadData);
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
PSP_DEBUG("sceKernelCreateThread: PTE_OS_NO_RESOURCES\n");
|
||||
result = PTE_OS_NO_RESOURCES;
|
||||
}
|
||||
else if (threadId < 0)
|
||||
{
|
||||
free(pThreadData);
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
PSP_DEBUG("sceKernelCreateThread: PTE_OS_GENERAL_FAILURE\n");
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppte_osThreadHandle = threadId;
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
|
||||
FAIL0:
|
||||
return result;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle)
|
||||
{
|
||||
sceKernelStartThread(osThreadHandle, 0, NULL);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osThreadDelete(pte_osThreadHandle handle)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(handle);
|
||||
pspThreadData *pThreadData = getThreadData(handle);
|
||||
|
||||
sceKernelDeleteSema(pThreadData->cancelSem);
|
||||
|
||||
free(pThreadData);
|
||||
|
||||
pteTlsThreadDestroy(pTls);
|
||||
|
||||
sceKernelDeleteThread(handle);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle)
|
||||
{
|
||||
pte_osThreadDelete(handle);
|
||||
|
||||
sceKernelExitDeleteThread(0);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
void pte_osThreadExit(void)
|
||||
{
|
||||
sceKernelExitThread(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This has to be cancellable, so we can't just call sceKernelWaitThreadEnd.
|
||||
* Instead, poll on this in a loop, like we do for a cancellable semaphore.
|
||||
*/
|
||||
pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData = getThreadData(sceKernelGetThreadId());
|
||||
|
||||
while (1)
|
||||
{
|
||||
SceKernelThreadRunStatus info;
|
||||
|
||||
/* Poll task to see if it has ended */
|
||||
memset(&info,0,sizeof(info));
|
||||
info.size = sizeof(info);
|
||||
sceKernelReferThreadRunStatus(threadHandle, &info);
|
||||
|
||||
if (info.status == PSP_THREAD_STOPPED)
|
||||
{
|
||||
/* Thread has ended */
|
||||
result = PTE_OS_OK;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceKernelSemaInfo semInfo;
|
||||
|
||||
if (pThreadData != NULL)
|
||||
{
|
||||
SceUID osResult;
|
||||
|
||||
osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
if (semInfo.currentCount > 0)
|
||||
{
|
||||
result = PTE_OS_INTERRUPTED;
|
||||
break;
|
||||
}
|
||||
/* Nothing found and not timed out yet; let's yield so we're not
|
||||
* in busy loop.
|
||||
*/
|
||||
else
|
||||
sceKernelDelayThread(POLLING_DELAY_IN_us);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pte_osThreadHandle pte_osThreadGetHandle(void)
|
||||
{
|
||||
return sceKernelGetThreadId();
|
||||
}
|
||||
|
||||
int pte_osThreadGetPriority(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
SceKernelThreadInfo thinfo;
|
||||
|
||||
thinfo.size = sizeof(SceKernelThreadInfo);
|
||||
|
||||
sceKernelReferThreadStatus(threadHandle, &thinfo);
|
||||
|
||||
return thinfo.currentPriority;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority)
|
||||
{
|
||||
sceKernelChangeThreadPriority(threadHandle, newPriority);
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
pspThreadData *pThreadData = getThreadData(threadHandle);
|
||||
SceUID osResult = sceKernelSignalSema(pThreadData->cancelSem, 1);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
return PTE_OS_OK;
|
||||
|
||||
return PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle)
|
||||
{
|
||||
SceKernelSemaInfo semInfo;
|
||||
SceUID osResult;
|
||||
pte_osResult result;
|
||||
pspThreadData *pThreadData = getThreadData(threadHandle);
|
||||
|
||||
if (pThreadData != NULL)
|
||||
{
|
||||
osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
if (semInfo.currentCount > 0)
|
||||
result = PTE_OS_INTERRUPTED;
|
||||
else
|
||||
result = PTE_OS_OK;
|
||||
}
|
||||
/* sceKernelReferSemaStatus returned an error */
|
||||
else
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
/* For some reason, we couldn't get thread data */
|
||||
else
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pte_osThreadSleep(unsigned int msecs)
|
||||
{
|
||||
sceKernelDelayThread(msecs*1000);
|
||||
}
|
||||
|
||||
int pte_osThreadGetMinPriority(void)
|
||||
{
|
||||
return 17;
|
||||
}
|
||||
|
||||
int pte_osThreadGetMaxPriority(void)
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
int pte_osThreadGetDefaultPriority(void)
|
||||
{
|
||||
return 18;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Mutexes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle)
|
||||
{
|
||||
static int mutexCtr = 0;
|
||||
char mutexName[32];
|
||||
pte_osMutexHandle handle;
|
||||
|
||||
if (mutexCtr++ > MAX_PSP_UID)
|
||||
mutexCtr = 0;
|
||||
|
||||
snprintf(mutexName,sizeof(mutexName),"mutex%d",mutexCtr);
|
||||
|
||||
handle = sceKernelCreateSema(mutexName,
|
||||
0, /* attributes (default) */
|
||||
1, /* initial value */
|
||||
1, /* maximum value */
|
||||
0); /* options (default) */
|
||||
|
||||
*pHandle = handle;
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osMutexDelete(pte_osMutexHandle handle)
|
||||
{
|
||||
sceKernelDeleteSema(handle);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osMutexLock(pte_osMutexHandle handle)
|
||||
{
|
||||
sceKernelWaitSema(handle, 1, NULL);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs)
|
||||
{
|
||||
SceUInt timeoutUsecs = timeoutMsecs*1000;
|
||||
int status = sceKernelWaitSema(handle, 1, &timeoutUsecs);
|
||||
|
||||
/* Assume that any error from sceKernelWaitSema was due to a timeout */
|
||||
if (status < 0)
|
||||
return PTE_OS_TIMEOUT;
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle)
|
||||
{
|
||||
sceKernelSignalSema(handle, 1);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Semaphores
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle)
|
||||
{
|
||||
pte_osSemaphoreHandle handle;
|
||||
static int semCtr = 0;
|
||||
char semName[32];
|
||||
|
||||
if (semCtr++ > MAX_PSP_UID)
|
||||
semCtr = 0;
|
||||
|
||||
snprintf(semName,sizeof(semName),"pthread_sem%d",semCtr);
|
||||
|
||||
handle = sceKernelCreateSema(semName,
|
||||
0, /* attributes (default) */
|
||||
initialValue, /* initial value */
|
||||
SEM_VALUE_MAX, /* maximum value */
|
||||
0); /* options (default) */
|
||||
|
||||
*pHandle = handle;
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle)
|
||||
{
|
||||
sceKernelDeleteSema(handle);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count)
|
||||
{
|
||||
sceKernelSignalSema(handle, count);
|
||||
|
||||
return PTE_OS_OK;
|
||||
}
|
||||
|
||||
pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeoutMsecs)
|
||||
{
|
||||
unsigned int timeoutUsecs;
|
||||
unsigned int *pTimeoutUsecs;
|
||||
SceUInt result;
|
||||
pte_osResult osResult;
|
||||
|
||||
if (pTimeoutMsecs == NULL)
|
||||
pTimeoutUsecs = NULL;
|
||||
else
|
||||
{
|
||||
timeoutUsecs = *pTimeoutMsecs * 1000;
|
||||
pTimeoutUsecs = &timeoutUsecs;
|
||||
}
|
||||
|
||||
result = sceKernelWaitSema(handle, 1, pTimeoutUsecs);
|
||||
|
||||
if (result == SCE_KERNEL_ERROR_OK)
|
||||
return PTE_OS_OK;
|
||||
|
||||
if (result == SCE_KERNEL_ERROR_WAIT_TIMEOUT)
|
||||
return PTE_OS_TIMEOUT;
|
||||
|
||||
return PTE_OS_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pend on a semaphore- and allow the pend to be cancelled.
|
||||
*
|
||||
* PSP OS provides no functionality to asynchronously interrupt a blocked call. We simulte
|
||||
* this by polling on the main semaphore and the cancellation semaphore and sleeping in a loop.
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle semHandle, unsigned int *pTimeout)
|
||||
{
|
||||
pspThreadData *pThreadData;
|
||||
|
||||
pThreadData = getThreadData(sceKernelGetThreadId());
|
||||
|
||||
clock_t start_time;
|
||||
pte_osResult result = PTE_OS_OK;
|
||||
unsigned int timeout;
|
||||
unsigned char timeoutEnabled;
|
||||
|
||||
start_time = clock();
|
||||
|
||||
// clock() is in microseconds, timeout as passed in was in milliseconds
|
||||
if (pTimeout == NULL)
|
||||
{
|
||||
timeout = 0;
|
||||
timeoutEnabled = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeout = *pTimeout * 1000;
|
||||
timeoutEnabled = 1;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
SceUInt semTimeout;
|
||||
int status;
|
||||
|
||||
/* Poll semaphore */
|
||||
semTimeout = 0;
|
||||
status = sceKernelWaitSema(semHandle, 1, &semTimeout);
|
||||
|
||||
if (status == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
/* User semaphore posted to */
|
||||
result = PTE_OS_OK;
|
||||
break;
|
||||
}
|
||||
else if ((timeoutEnabled) && ((clock() - start_time) > timeout))
|
||||
{
|
||||
/* Timeout expired */
|
||||
result = PTE_OS_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
SceKernelSemaInfo semInfo;
|
||||
|
||||
if (pThreadData != NULL)
|
||||
{
|
||||
SceUID osResult;
|
||||
|
||||
osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo);
|
||||
|
||||
if (osResult == SCE_KERNEL_ERROR_OK)
|
||||
{
|
||||
if (semInfo.currentCount > 0)
|
||||
{
|
||||
result = PTE_OS_INTERRUPTED;
|
||||
break;
|
||||
}
|
||||
/* Nothing found and not timed out yet; let's yield so we're not
|
||||
* in busy loop.
|
||||
*/
|
||||
else
|
||||
sceKernelDelayThread(POLLING_DELAY_IN_us);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = PTE_OS_GENERAL_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Atomic Operations
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
int pte_osAtomicExchange(int *ptarg, int val)
|
||||
{
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
int origVal = *ptarg;
|
||||
|
||||
*ptarg = val;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return origVal;
|
||||
|
||||
}
|
||||
|
||||
int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp)
|
||||
{
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
int origVal = *pdest;
|
||||
|
||||
if (*pdest == comp)
|
||||
*pdest = exchange;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return origVal;
|
||||
}
|
||||
|
||||
|
||||
int pte_osAtomicExchangeAdd(int volatile* pAddend, int value)
|
||||
{
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
int origVal = *pAddend;
|
||||
|
||||
*pAddend += value;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return origVal;
|
||||
}
|
||||
|
||||
int pte_osAtomicDecrement(int *pdest)
|
||||
{
|
||||
int val;
|
||||
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
|
||||
(*pdest)--;
|
||||
val = *pdest;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int pte_osAtomicIncrement(int *pdest)
|
||||
{
|
||||
int val;
|
||||
|
||||
int intc = pspSdkDisableInterrupts();
|
||||
|
||||
(*pdest)++;
|
||||
val = *pdest;
|
||||
|
||||
pspSdkEnableInterrupts(intc);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Helper functions
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
static pspThreadData *getThreadData(SceUID threadHandle)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(threadHandle);
|
||||
pspThreadData *pThreadData = (pspThreadData *)
|
||||
pteTlsGetValue(pTls, threadDataKey);
|
||||
|
||||
return pThreadData;
|
||||
}
|
||||
|
||||
static void *getTlsStructFromThread(SceUID thid)
|
||||
{
|
||||
SceKernelThreadInfo thinfo;
|
||||
unsigned int ptr;
|
||||
unsigned int thrNum;
|
||||
int numMatches;
|
||||
|
||||
thinfo.size = sizeof(SceKernelThreadInfo);
|
||||
|
||||
sceKernelReferThreadStatus(thid, &thinfo);
|
||||
|
||||
numMatches = sscanf(thinfo.name,"pthread%04d__%x", &thrNum, &ptr);
|
||||
|
||||
/* If we were called from a pthread, use the TLS allocated when the thread
|
||||
* was created. Otherwise, we were called from a non-pthread, so use the
|
||||
* "global". This is a pretty bad hack, but necessary due to lack of TLS on PSP.
|
||||
*/
|
||||
if (numMatches == 2)
|
||||
return (void *) ptr;
|
||||
|
||||
return globalTls;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Thread Local Storage
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
pte_osResult pte_osTlsSetValue(unsigned int key, void * value)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(sceKernelGetThreadId());
|
||||
|
||||
return pteTlsSetValue(pTls, key, value);
|
||||
}
|
||||
|
||||
void * pte_osTlsGetValue(unsigned int index)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(sceKernelGetThreadId());
|
||||
|
||||
return (void *) pteTlsGetValue(pTls, index);
|
||||
|
||||
}
|
||||
|
||||
|
||||
pte_osResult pte_osTlsAlloc(unsigned int *pKey)
|
||||
{
|
||||
void *pTls = getTlsStructFromThread(sceKernelGetThreadId());
|
||||
|
||||
return pteTlsAlloc(pKey);
|
||||
|
||||
}
|
||||
|
||||
pte_osResult pte_osTlsFree(unsigned int index)
|
||||
{
|
||||
return pteTlsFree(index);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Miscellaneous
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
int ftime(struct timeb *tb)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
|
||||
tb->time = tv.tv_sec;
|
||||
tb->millitm = tv.tv_usec / 1000;
|
||||
tb->timezone = tz.tz_minuteswest;
|
||||
tb->dstflag = tz.tz_dsttime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* psp_osal.h
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <pspsdk.h>
|
||||
|
||||
typedef SceUID pte_osThreadHandle;
|
||||
|
||||
typedef SceUID pte_osSemaphoreHandle;
|
||||
|
||||
typedef SceUID pte_osMutexHandle;
|
||||
|
||||
|
||||
#define OS_IS_HANDLE_VALID(x) ((x) > 0)
|
||||
|
||||
#define OS_MAX_SIMUL_THREADS 10
|
||||
|
||||
#define OS_DEFAULT_PRIO 11
|
||||
|
||||
#define OS_MIN_PRIO 17
|
||||
#define OS_MAX_PRIO 32
|
||||
|
||||
#if 0
|
||||
#define HAVE_THREAD_SAFE_ERRNO
|
||||
#endif
|
||||
|
||||
#define POLLING_DELAY_IN_us 100
|
||||
|
||||
#define OS_MAX_SEM_VALUE 254
|
||||
|
||||
int PspInterlockedExchange(int *ptarg, int val);
|
||||
int PspInterlockedCompareExchange(int *pdest, int exchange, int comp);
|
||||
int PspInterlockedExchangeAdd(int volatile* pAddend, int value);
|
||||
int PspInterlockedDecrement(int *pdest);
|
||||
int PspInterlockedIncrement(int *pdest);
|
||||
|
||||
|
||||
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
#ifndef _OS_SUPPORT_H_
|
||||
#define _OS_SUPPORT_H_
|
||||
|
||||
/* Platform specific one must be included first */
|
||||
#include "psp_osal.h"
|
||||
|
||||
#include "pte_generic_osal.h"
|
||||
|
||||
#endif /* _OS_SUPPORT_H */
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
/* pte_types.h */
|
||||
|
||||
#ifndef PTE_TYPES_H
|
||||
#define PTE_TYPES_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
typedef int pid_t;
|
||||
|
||||
#endif /* PTE_TYPES_H */
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pspkernel.h>
|
||||
#include <pspdebug.h>
|
||||
#include <pspsdk.h>
|
||||
#include <pspctrl.h>
|
||||
|
||||
PSP_MODULE_INFO("Pthread Test", 0, 1, 1);
|
||||
|
||||
extern void pte_test_main();
|
||||
|
||||
#ifdef JNS
|
||||
#define printf pspDebugScreenPrintf
|
||||
#endif
|
||||
|
||||
/* Exit callback */
|
||||
int exit_callback(int arg1, int arg2, void *common)
|
||||
{
|
||||
sceKernelExitGame();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback thread */
|
||||
int CallbackThread(SceSize args, void *argp)
|
||||
{
|
||||
int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
|
||||
sceKernelRegisterExitCallback(cbid);
|
||||
|
||||
sceKernelSleepThreadCB();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets up the callback thread and returns its thread id */
|
||||
int SetupCallbacks(void)
|
||||
{
|
||||
int thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
|
||||
if (thid >= 0)
|
||||
sceKernelStartThread(thid, 0, 0);
|
||||
|
||||
return thid;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
SceCtrlData pad;
|
||||
|
||||
pspDebugScreenInit();
|
||||
SetupCallbacks();
|
||||
|
||||
pte_test_main();
|
||||
|
||||
while (1)
|
||||
{
|
||||
sceCtrlReadBufferPositive(&pad, 1);
|
||||
if (pad.Buttons & PSP_CTRL_UP)
|
||||
{
|
||||
printf("Exiting...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Vendored
-1059
File diff suppressed because it is too large.
Load diff
Vendored
-449
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
* pte_cancellable_wait.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef _GENERIC_OS_SUPPORT_H_
|
||||
#define _GENERIC_OS_SUPPORT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/** @name Misc */
|
||||
//@{
|
||||
typedef enum pte_osResult
|
||||
{
|
||||
|
||||
/** Operation completed successfully */
|
||||
PTE_OS_OK = 0,
|
||||
|
||||
/** Operation failed because there insufficient resources */
|
||||
PTE_OS_NO_RESOURCES,
|
||||
|
||||
/** Operation failed due to a general failure */
|
||||
PTE_OS_GENERAL_FAILURE,
|
||||
|
||||
/** Operation did not complete because a user specified timeout expired. */
|
||||
PTE_OS_TIMEOUT,
|
||||
|
||||
/** The operation was interrupted before it could complete. */
|
||||
PTE_OS_INTERRUPTED,
|
||||
|
||||
/** An invalid parameter was specified */
|
||||
PTE_OS_INVALID_PARAM
|
||||
|
||||
|
||||
} pte_osResult;
|
||||
|
||||
/**
|
||||
* Provides a hook for the OSAL to implement any OS specific initialization. This is guaranteed to be
|
||||
* called before any other OSAL function.
|
||||
*/
|
||||
pte_osResult pte_osInit(void);
|
||||
//@}
|
||||
|
||||
/** @name Mutexes */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Creates a mutex
|
||||
*
|
||||
* @param pHandle Set to the handle of the newly created mutex.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully created
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to create mutex
|
||||
*/
|
||||
pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle);
|
||||
|
||||
/**
|
||||
* Deletes a mutex and frees any associated resources.
|
||||
*
|
||||
* @param handle Handle of mutex to delete.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully deleted.
|
||||
*/
|
||||
pte_osResult pte_osMutexDelete(pte_osMutexHandle handle);
|
||||
|
||||
/**
|
||||
* Locks the mutex
|
||||
*
|
||||
* @param handle Handle of mutex to lock.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully locked.
|
||||
*/
|
||||
pte_osResult pte_osMutexLock(pte_osMutexHandle handle);
|
||||
|
||||
/**
|
||||
* Locks the mutex, returning after @p timeoutMsecs if the resources is not
|
||||
* available. Can be used for polling mutex by using @p timeoutMsecs of zero.
|
||||
*
|
||||
* @param handle Handle of mutex to lock.
|
||||
* @param timeoutMsecs Number of milliseconds to wait for resource before returning.
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully locked.
|
||||
* @return PTE_OS_TIMEOUT - Timeout expired before lock was obtained.
|
||||
*/
|
||||
pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs);
|
||||
|
||||
/**
|
||||
* Unlocks the mutex
|
||||
*
|
||||
* @param handle Handle of mutex to unlock
|
||||
*
|
||||
* @return PTE_OS_OK - Mutex successfully unlocked.
|
||||
*/
|
||||
pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle);
|
||||
//@}
|
||||
|
||||
/** @name Threads */
|
||||
//@{
|
||||
typedef int (*pte_osThreadEntryPoint)(void *params);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new thread. The thread must be started in a suspended state - it will be
|
||||
* explicitly started when pte_osThreadStart() is called.
|
||||
*
|
||||
* @param entryPoint Entry point to the new thread.
|
||||
* @param stackSize The initial stack size, in bytes. Note that this can be considered a minimum -
|
||||
* for instance if the OS requires a larger stack space than what the caller specified.
|
||||
* @param initialPriority The priority that the new thread should be initially set to.
|
||||
* @param argv Parameter to pass to the new thread.
|
||||
* @param ppte_osThreadHandle set to the handle of the new thread.
|
||||
*
|
||||
* @return PTE_OS_OK - New thread successfully created.
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to create thread
|
||||
*/
|
||||
pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint,
|
||||
int stackSize,
|
||||
int initialPriority,
|
||||
void *argv,
|
||||
pte_osThreadHandle* ppte_osThreadHandle);
|
||||
|
||||
/**
|
||||
* Starts executing the specified thread.
|
||||
*
|
||||
* @param osThreadHandle handle of the thread to start.
|
||||
*
|
||||
* @return PTE_OS_OK - thread successfully started.
|
||||
*/
|
||||
pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle);
|
||||
|
||||
/**
|
||||
* Causes the current thread to stop executing.
|
||||
*
|
||||
* @return Never returns (thread terminated)
|
||||
*/
|
||||
void pte_osThreadExit();
|
||||
|
||||
/**
|
||||
* Waits for the specified thread to end. If the thread has already terminated, this returns
|
||||
* immediately.
|
||||
*
|
||||
* @param threadHandle Handle fo thread to wait for.
|
||||
*
|
||||
* @return PTE_OS_OK - specified thread terminated.
|
||||
*/
|
||||
pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Returns the handle of the currently executing thread.
|
||||
*/
|
||||
pte_osThreadHandle pte_osThreadGetHandle(void);
|
||||
|
||||
/**
|
||||
* Returns the priority of the specified thread.
|
||||
*/
|
||||
int pte_osThreadGetPriority(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Sets the priority of the specified thread.
|
||||
*
|
||||
* @return PTE_OS_OK - thread priority successfully set
|
||||
*/
|
||||
pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority);
|
||||
|
||||
/**
|
||||
* Frees resources associated with the specified thread. This is called after the thread has terminated
|
||||
* and is no longer needed (e.g. after pthread_join returns). This call will always be made
|
||||
* from a different context than that of the target thread.
|
||||
*/
|
||||
pte_osResult pte_osThreadDelete(pte_osThreadHandle handle);
|
||||
|
||||
/**
|
||||
* Frees resources associated with the specified thread and then causes the thread to exit.
|
||||
* This is called after the thread has terminated and is no longer needed (e.g. after
|
||||
* pthread_join returns). This call will always be made from the context of the target thread.
|
||||
*/
|
||||
pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle);
|
||||
|
||||
/**
|
||||
* Cancels the specified thread. This should cause pte_osSemaphoreCancellablePend() and for pte_osThreadCheckCancel()
|
||||
* to return @p PTE_OS_INTERRUPTED.
|
||||
*
|
||||
* @param threadHandle handle to the thread to cancel.
|
||||
*
|
||||
* @return Thread successfully canceled.
|
||||
*/
|
||||
pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Check if pte_osThreadCancel() has been called on the specified thread.
|
||||
*
|
||||
* @param threadHandle handle of thread to check the state of.
|
||||
*
|
||||
* @return PTE_OS_OK - Thread has not been cancelled
|
||||
* @return PTE_OS_INTERRUPTED - Thread has been cancelled.
|
||||
*/
|
||||
pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle);
|
||||
|
||||
/**
|
||||
* Causes the current thread to sleep for the specified number of milliseconds.
|
||||
*/
|
||||
void pte_osThreadSleep(unsigned int msecs);
|
||||
|
||||
/**
|
||||
* Returns the maximum allowable priority
|
||||
*/
|
||||
int pte_osThreadGetMaxPriority();
|
||||
|
||||
/**
|
||||
* Returns the minimum allowable priority
|
||||
*/
|
||||
int pte_osThreadGetMinPriority();
|
||||
|
||||
/**
|
||||
* Returns the priority that should be used if the caller to pthread_create doesn't
|
||||
* explicitly set one.
|
||||
*/
|
||||
int pte_osThreadGetDefaultPriority();
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/** @name Semaphores */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Creates a semaphore
|
||||
*
|
||||
* @param initialValue Initial value of the semaphore
|
||||
* @param pHandle Set to the handle of the newly created semaphore.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully created
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to create semaphore
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle);
|
||||
|
||||
/**
|
||||
* Deletes a semaphore and frees any associated resources.
|
||||
*
|
||||
* @param handle Handle of semaphore to delete.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully deleted.
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle);
|
||||
|
||||
/**
|
||||
* Posts to the semaphore
|
||||
*
|
||||
* @param handle Semaphore to release
|
||||
* @param count Amount to increment the semaphore by.
|
||||
*
|
||||
* @return PTE_OS_OK - semaphore successfully released.
|
||||
*/
|
||||
pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count);
|
||||
|
||||
/**
|
||||
* Acquire a semaphore, returning after @p timeoutMsecs if the semaphore is not
|
||||
* available. Can be used for polling a semaphore by using @p timeoutMsecs of zero.
|
||||
*
|
||||
* @param handle Handle of semaphore to acquire.
|
||||
* @param pTimeout Pointer to the number of milliseconds to wait to acquire the semaphore
|
||||
* before returning. If set to NULL, wait forever.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully acquired.
|
||||
* @return PTE_OS_TIMEOUT - Timeout expired before semaphore was obtained.
|
||||
*/
|
||||
pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeout);
|
||||
|
||||
/**
|
||||
* Acquire a semaphore, returning after @p timeoutMsecs if the semaphore is not
|
||||
* available. Can be used for polling a semaphore by using @p timeoutMsecs of zero.
|
||||
* Call must return immediately if pte_osThreadCancel() is called on the thread waiting for
|
||||
* the semaphore.
|
||||
*
|
||||
* @param handle Handle of semaphore to acquire.
|
||||
* @param pTimeout Pointer to the number of milliseconds to wait to acquire the semaphore
|
||||
* before returning. If set to NULL, wait forever.
|
||||
*
|
||||
* @return PTE_OS_OK - Semaphore successfully acquired.
|
||||
* @return PTE_OS_TIMEOUT - Timeout expired before semaphore was obtained.
|
||||
*/
|
||||
pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle handle, unsigned int *pTimeout);
|
||||
//@}
|
||||
|
||||
|
||||
/** @name Thread Local Storage */
|
||||
//@{
|
||||
/**
|
||||
* Sets the thread specific value for the specified key for the
|
||||
* currently executing thread.
|
||||
*
|
||||
* @param index The TLS key for the value.
|
||||
* @param value The value to save
|
||||
*/
|
||||
pte_osResult pte_osTlsSetValue(unsigned int key, void * value);
|
||||
|
||||
/**
|
||||
* Retrieves the thread specific value for the specified key for
|
||||
* the currently executing thread. If a value has not been set
|
||||
* for this key, NULL should be returned (i.e. TLS values default
|
||||
* to NULL).
|
||||
*
|
||||
* @param index The TLS key for the value.
|
||||
*
|
||||
* @return The value associated with @p key for the current thread.
|
||||
*/
|
||||
void * pte_osTlsGetValue(unsigned int key);
|
||||
|
||||
/**
|
||||
* Initializes the OS TLS support. This is called by the PTE library
|
||||
* prior to performing ANY TLS operation.
|
||||
*/
|
||||
void pte_osTlsInit(void);
|
||||
|
||||
/**
|
||||
* Allocates a new TLS key.
|
||||
*
|
||||
* @param pKey On success will be set to the newly allocated key.
|
||||
*
|
||||
* @return PTE_OS_OK - TLS key successfully allocated.
|
||||
* @return PTE_OS_NO_RESOURCESs - Insufficient resources to allocate key (e.g.
|
||||
* maximum number of keys reached).
|
||||
*/
|
||||
pte_osResult pte_osTlsAlloc(unsigned int *pKey);
|
||||
|
||||
/**
|
||||
* Frees the specified TLS key.
|
||||
*
|
||||
* @param index TLS key to free
|
||||
*
|
||||
* @return PTE_OS_OK - TLS key was successfully freed.
|
||||
*/
|
||||
pte_osResult pte_osTlsFree(unsigned int key);
|
||||
//@}
|
||||
|
||||
/** @name Atomic operations */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* Sets the target to the specified value as an atomic operation.
|
||||
*
|
||||
* \code
|
||||
* origVal = *ptarg
|
||||
* *ptarg = val
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pTarg Pointer to the value to be exchanged.
|
||||
* @param val Value to be exchanged
|
||||
*
|
||||
* @return original value of destination
|
||||
*/
|
||||
int pte_osAtomicExchange(int *pTarg, int val);
|
||||
|
||||
/**
|
||||
* Performs an atomic compare-and-exchange oepration on the specified
|
||||
* value. That is:
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest
|
||||
* if (*pdest == comp)
|
||||
* then *pdest = exchange
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pdest Pointer to the destination value.
|
||||
* @param exchange Exchange value (value to set destination to if destination == comparand)
|
||||
* @param comp The value to compare to destination.
|
||||
*
|
||||
* @return Original value of destination
|
||||
*/
|
||||
int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp);
|
||||
|
||||
/**
|
||||
* Adds the value to target as an atomic operation
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest
|
||||
* *pAddend += value
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pdest Pointer to the variable to be updated.
|
||||
* @param value Value to be added to the variable.
|
||||
*
|
||||
* @return Original value of destination
|
||||
*/
|
||||
int pte_osAtomicExchangeAdd(int volatile* pdest, int value);
|
||||
|
||||
/**
|
||||
* Decrements the destination.
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest
|
||||
* *pdest++
|
||||
* return origVal
|
||||
* \endcode
|
||||
*
|
||||
* @param pdest Destination value to decrement
|
||||
*
|
||||
* @return Original destination value
|
||||
*/
|
||||
int pte_osAtomicDecrement(int *pdest);
|
||||
|
||||
/**
|
||||
* Increments the destination value
|
||||
*
|
||||
* \code
|
||||
* origVal = *pdest;
|
||||
* *pdest++;
|
||||
* return origVal;
|
||||
*/
|
||||
int pte_osAtomicIncrement(int *pdest);
|
||||
//@}
|
||||
|
||||
struct timeb;
|
||||
|
||||
int ftime(struct timeb *tb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _OS_SUPPORT_H_
|
||||
Vendored
-396
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* pte_main.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit instantiates data associated with the implementation
|
||||
* as a whole.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int pte_processInitialized = PTE_FALSE;
|
||||
pte_thread_t * pte_threadReuseTop = PTE_THREAD_REUSE_EMPTY;
|
||||
pte_thread_t * pte_threadReuseBottom = PTE_THREAD_REUSE_EMPTY;
|
||||
pthread_key_t pte_selfThreadKey = NULL;
|
||||
pthread_key_t pte_cleanupKey = NULL;
|
||||
pthread_cond_t pte_cond_list_head = NULL;
|
||||
pthread_cond_t pte_cond_list_tail = NULL;
|
||||
|
||||
int pte_concurrency = 0;
|
||||
|
||||
/* What features have been auto-detaected */
|
||||
int pte_features = 0;
|
||||
|
||||
unsigned char pte_smp_system = PTE_TRUE; /* Safer if assumed true initially. */
|
||||
|
||||
/*
|
||||
* Global lock for managing pthread_t struct reuse.
|
||||
*/
|
||||
pte_osMutexHandle pte_thread_reuse_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of statically declared mutexes.
|
||||
*/
|
||||
pte_osMutexHandle pte_mutex_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of PTHREAD_COND_INITIALIZER
|
||||
* created condition variables.
|
||||
*/
|
||||
pte_osMutexHandle pte_cond_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of PTHREAD_RWLOCK_INITIALIZER
|
||||
* created read/write locks.
|
||||
*/
|
||||
pte_osMutexHandle pte_rwlock_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for testing internal state of PTHREAD_SPINLOCK_INITIALIZER
|
||||
* created spin locks.
|
||||
*/
|
||||
pte_osMutexHandle pte_spinlock_test_init_lock;
|
||||
|
||||
/*
|
||||
* Global lock for condition variable linked list. The list exists
|
||||
* to wake up CVs when a WM_TIMECHANGE message arrives. See
|
||||
* w32_TimeChangeHandler.c.
|
||||
*/
|
||||
pte_osMutexHandle pte_cond_list_lock;
|
||||
|
||||
int pthread_create (pthread_t * tid,
|
||||
const pthread_attr_t * attr,
|
||||
void *(*start) (void *), void *arg)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function creates a thread running the start function,
|
||||
* passing it the parameter value, 'arg'. The 'attr'
|
||||
* argument specifies optional creation attributes.
|
||||
* The identity of the new thread is returned
|
||||
* via 'tid', which should not be NULL.
|
||||
*
|
||||
* PARAMETERS
|
||||
* tid
|
||||
* pointer to an instance of pthread_t
|
||||
*
|
||||
* attr
|
||||
* optional pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* start
|
||||
* pointer to the starting routine for the new thread
|
||||
*
|
||||
* arg
|
||||
* optional parameter passed to 'start'
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function creates a thread running the start function,
|
||||
* passing it the parameter value, 'arg'. The 'attr'
|
||||
* argument specifies optional creation attributes.
|
||||
* The identity of the new thread is returned
|
||||
* via 'tid', which should not be the NULL pointer.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created thread,
|
||||
* EINVAL attr invalid,
|
||||
* EAGAIN insufficient resources.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_t thread;
|
||||
pte_thread_t * tp;
|
||||
int result = EAGAIN;
|
||||
int run = PTE_TRUE;
|
||||
ThreadParms *parms = NULL;
|
||||
long stackSize;
|
||||
int priority = 0;
|
||||
pthread_t self;
|
||||
pte_osResult osResult;
|
||||
register pthread_attr_t a = NULL;
|
||||
|
||||
/*
|
||||
* Before doing anything, check that tid can be stored through
|
||||
* without invoking a memory protection error (segfault).
|
||||
* Make sure that the assignment below can't be optimised out by the compiler.
|
||||
* This is assured by conditionally assigning *tid again at the end.
|
||||
*/
|
||||
|
||||
if (attr != NULL)
|
||||
a = *attr;
|
||||
|
||||
if ((thread = pte_new ()) == NULL)
|
||||
goto FAIL0;
|
||||
|
||||
tp = (pte_thread_t *) thread;
|
||||
|
||||
priority = tp->sched_priority;
|
||||
|
||||
if ((parms = (ThreadParms *) malloc (sizeof (*parms))) == NULL)
|
||||
goto FAIL0;
|
||||
|
||||
parms->tid = thread;
|
||||
parms->start = start;
|
||||
parms->arg = arg;
|
||||
|
||||
if (a != NULL)
|
||||
{
|
||||
stackSize = a->stacksize;
|
||||
tp->detachState = a->detachstate;
|
||||
priority = a->param.sched_priority;
|
||||
|
||||
if ( (priority > pte_osThreadGetMaxPriority()) ||
|
||||
(priority < pte_osThreadGetMinPriority()) )
|
||||
{
|
||||
result = EINVAL;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/* Everything else */
|
||||
|
||||
/*
|
||||
* Thread priority must be set to a valid system level
|
||||
* without altering the value set by pthread_attr_setschedparam().
|
||||
*/
|
||||
|
||||
if (PTHREAD_INHERIT_SCHED == a->inheritsched)
|
||||
{
|
||||
/*
|
||||
* If the thread that called pthread_create() is an OS thread
|
||||
* then the inherited priority could be the result of a temporary
|
||||
* system adjustment. This is not the case for POSIX threads.
|
||||
*/
|
||||
self = pthread_self ();
|
||||
priority = ((pte_thread_t *) self)->sched_priority;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Default stackSize
|
||||
*/
|
||||
stackSize = PTHREAD_STACK_MIN;
|
||||
|
||||
}
|
||||
|
||||
tp->state = run ? PThreadStateInitial : PThreadStateSuspended;
|
||||
|
||||
tp->keys = NULL;
|
||||
|
||||
/*
|
||||
* Threads must be started in suspended mode and resumed if necessary
|
||||
* after _beginthreadex returns us the handle. Otherwise we set up a
|
||||
* race condition between the creating and the created threads.
|
||||
* Note that we also retain a local copy of the handle for use
|
||||
* by us in case thread.p->threadH gets NULLed later but before we've
|
||||
* finished with it here.
|
||||
*/
|
||||
result = pthread_mutex_lock (&tp->threadLock);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
/*
|
||||
* Must record the thread's sched_priority as given,
|
||||
* not as finally adjusted.
|
||||
*/
|
||||
tp->sched_priority = priority;
|
||||
|
||||
(void) pthread_mutex_unlock (&tp->threadLock);
|
||||
}
|
||||
|
||||
osResult = pte_osThreadCreate(pte_threadStart,
|
||||
stackSize,
|
||||
priority,
|
||||
parms,
|
||||
&(tp->threadId));
|
||||
|
||||
if (osResult == PTE_OS_OK)
|
||||
{
|
||||
pte_osThreadStart(tp->threadId);
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp->threadId = 0;
|
||||
result = EAGAIN;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fall Through Intentionally
|
||||
*/
|
||||
|
||||
/*
|
||||
* ------------
|
||||
* Failure Code
|
||||
* ------------
|
||||
*/
|
||||
|
||||
FAIL0:
|
||||
if (result != 0)
|
||||
{
|
||||
|
||||
pte_threadDestroy (thread);
|
||||
tp = NULL;
|
||||
|
||||
if (parms != NULL)
|
||||
free (parms);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tid != NULL)
|
||||
*tid = thread;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions pte_pop_cleanup and pte_push_cleanup
|
||||
* are implemented here for applications written in C with no
|
||||
* C++ destructor support.
|
||||
*/
|
||||
|
||||
pte_cleanup_t *pte_pop_cleanup (int execute)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function pops the most recently pushed cleanup
|
||||
* handler. If execute is nonzero, then the cleanup handler
|
||||
* is executed if non-null.
|
||||
*
|
||||
* PARAMETERS
|
||||
* execute
|
||||
* if nonzero, execute the cleanup handler
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function pops the most recently pushed cleanup
|
||||
* handler. If execute is nonzero, then the cleanup handler
|
||||
* is executed if non-null.
|
||||
* NOTE: specify 'execute' as nonzero to avoid duplication
|
||||
* of common cleanup code.
|
||||
*
|
||||
* RESULTS
|
||||
* N/A
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pte_cleanup_t *cleanup;
|
||||
|
||||
cleanup = (pte_cleanup_t *) pthread_getspecific (pte_cleanupKey);
|
||||
|
||||
if (cleanup != NULL)
|
||||
{
|
||||
if (execute && (cleanup->routine != NULL))
|
||||
{
|
||||
|
||||
(*cleanup->routine) (cleanup->arg);
|
||||
|
||||
}
|
||||
|
||||
pthread_setspecific (pte_cleanupKey, (void *) cleanup->prev);
|
||||
|
||||
}
|
||||
|
||||
return (cleanup);
|
||||
|
||||
} /* pte_pop_cleanup */
|
||||
|
||||
|
||||
void
|
||||
pte_push_cleanup (pte_cleanup_t * cleanup,
|
||||
pte_cleanup_callback_t routine, void *arg)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function pushes a new cleanup handler onto the thread's stack
|
||||
* of cleanup handlers. Each cleanup handler pushed onto the stack is
|
||||
* popped and invoked with the argument 'arg' when
|
||||
* a) the thread exits by calling 'pthread_exit',
|
||||
* b) when the thread acts on a cancellation request,
|
||||
* c) or when the thread calls pthread_cleanup_pop with a nonzero
|
||||
* 'execute' argument
|
||||
*
|
||||
* PARAMETERS
|
||||
* cleanup
|
||||
* a pointer to an instance of pthread_cleanup_t,
|
||||
*
|
||||
* routine
|
||||
* pointer to a cleanup handler,
|
||||
*
|
||||
* arg
|
||||
* parameter to be passed to the cleanup handler
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function pushes a new cleanup handler onto the thread's stack
|
||||
* of cleanup handlers. Each cleanup handler pushed onto the stack is
|
||||
* popped and invoked with the argument 'arg' when
|
||||
* a) the thread exits by calling 'pthread_exit',
|
||||
* b) when the thread acts on a cancellation request,
|
||||
* c) or when the thrad calls pthread_cleanup_pop with a nonzero
|
||||
* 'execute' argument
|
||||
* NOTE: pthread_push_cleanup, pte_pop_cleanup must be paired
|
||||
* in the same lexical scope.
|
||||
*
|
||||
* RESULTS
|
||||
* pthread_cleanup_t *
|
||||
* pointer to the previous cleanup
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
cleanup->routine = routine;
|
||||
cleanup->arg = arg;
|
||||
|
||||
cleanup->prev = (pte_cleanup_t *) pthread_getspecific (pte_cleanupKey);
|
||||
|
||||
pthread_setspecific (pte_cleanupKey, (void *) cleanup);
|
||||
}
|
||||
Vendored
-1017
File diff suppressed because it is too large.
Load diff
Vendored
-891
@@ -1,891 +0,0 @@
|
||||
/* This is an implementation of the threads API of POSIX 1003.1-2001.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#if !defined( PTHREAD_H )
|
||||
#define PTHREAD_H
|
||||
|
||||
#include <pte_types.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#define PTE_VERSION 2,8,0,0
|
||||
#define PTE_VERSION_STRING "2, 8, 0, 0\0"
|
||||
|
||||
/* There are two implementations of cancel cleanup.
|
||||
* Note that pthread.h is included in both application
|
||||
* compilation units and also internally for the library.
|
||||
* The code here and within the library aims to work
|
||||
* for all reasonable combinations of environments.
|
||||
*
|
||||
* The two implementations are:
|
||||
*
|
||||
* C
|
||||
* C++
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define defaults for cleanup code.
|
||||
* Note: Unless the build explicitly defines one of the following, then
|
||||
* we default to standard C style cleanup. This style uses setjmp/longjmp
|
||||
* in the cancelation and thread exit implementations and therefore won't
|
||||
* do stack unwinding if linked to applications that have it (e.g.
|
||||
* C++ apps). This is currently consistent with most/all commercial Unix
|
||||
* POSIX threads implementations.
|
||||
*/
|
||||
|
||||
#undef PTE_LEVEL
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 1
|
||||
/* Include 1b, 1c and 1d */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
#define PTE_LEVEL_MAX 3
|
||||
|
||||
#if !defined(PTE_LEVEL)
|
||||
#define PTE_LEVEL PTE_LEVEL_MAX
|
||||
/* Include everything */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
*
|
||||
* Module: pthread.h
|
||||
*
|
||||
* Purpose:
|
||||
* Provides an implementation of PThreads based upon the
|
||||
* standard:
|
||||
*
|
||||
* POSIX 1003.1-2001
|
||||
* and
|
||||
* The Single Unix Specification version 3
|
||||
*
|
||||
* (these two are equivalent)
|
||||
*
|
||||
* in order to enhance code portability between Windows,
|
||||
* various commercial Unix implementations, and Linux.
|
||||
*
|
||||
* See the ANNOUNCE file for a full list of conforming
|
||||
* routines and defined constants, and a list of missing
|
||||
* routines and constants not defined in this implementation.
|
||||
*
|
||||
* Authors:
|
||||
* There have been many contributors to this library.
|
||||
* The initial implementation was contributed by
|
||||
* John Bossom, and several others have provided major
|
||||
* sections or revisions of parts of the implementation.
|
||||
* Often significant effort has been contributed to
|
||||
* find and fix important bugs and other problems to
|
||||
* improve the reliability of the library, which sometimes
|
||||
* is not reflected in the amount of code which changed as
|
||||
* result.
|
||||
* As much as possible, the contributors are acknowledged
|
||||
* in the ChangeLog file in the source code distribution
|
||||
* where their changes are noted in detail.
|
||||
*
|
||||
* Contributors are listed in the CONTRIBUTORS file.
|
||||
*
|
||||
* As usual, all bouquets go to the contributors, and all
|
||||
* brickbats go to the project maintainer.
|
||||
*
|
||||
* Maintainer:
|
||||
* The code base for this project is coordinated and
|
||||
* eventually pre-tested, packaged, and made available by
|
||||
*
|
||||
* Ross Johnson <rpj@callisto.canberra.edu.au>
|
||||
*
|
||||
* QA Testers:
|
||||
* Ultimately, the library is tested in the real world by
|
||||
* a host of competent and demanding scientists and
|
||||
* engineers who report bugs and/or provide solutions
|
||||
* which are then fixed or incorporated into subsequent
|
||||
* versions of the library. Each time a bug is fixed, a
|
||||
* test case is written to prove the fix and ensure
|
||||
* that later changes to the code don't reintroduce the
|
||||
* same error. The number of test cases is slowly growing
|
||||
* and therefore so is the code reliability.
|
||||
*
|
||||
* Compliance:
|
||||
* See the file ANNOUNCE for the list of implemented
|
||||
* and not-implemented routines and defined options.
|
||||
* Of course, these are all defined is this file as well.
|
||||
*
|
||||
* Web site:
|
||||
* The source code and other information about this library
|
||||
* are available from
|
||||
*
|
||||
* http://sources.redhat.com/pthreads-win32/
|
||||
*
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* Boolean values to make us independent of system includes.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
PTE_FALSE = 0,
|
||||
PTE_TRUE = (! PTE_FALSE)
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* POSIX 1003.1-2001 Options
|
||||
* =========================
|
||||
*
|
||||
* Options are normally set in <unistd.h>, which is not provided
|
||||
* with pthreads-embedded.
|
||||
*
|
||||
* For conformance with the Single Unix Specification (version 3), all of the
|
||||
* options below are defined, and have a value of either -1 (not supported)
|
||||
* or 200112L (supported).
|
||||
*
|
||||
* These options can neither be left undefined nor have a value of 0, because
|
||||
* either indicates that sysconf(), which is not implemented, may be used at
|
||||
* runtime to check the status of the option.
|
||||
*
|
||||
* _POSIX_THREADS (== 200112L)
|
||||
* If == 200112L, you can use threads
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)
|
||||
* If == 200112L, you can control the size of a thread's
|
||||
* stack
|
||||
* pthread_attr_getstacksize
|
||||
* pthread_attr_setstacksize
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKADDR (== -1)
|
||||
* If == 200112L, you can allocate and control a thread's
|
||||
* stack. If not supported, the following functions
|
||||
* will return ENOSYS, indicating they are not
|
||||
* supported:
|
||||
* pthread_attr_getstackaddr
|
||||
* pthread_attr_setstackaddr
|
||||
*
|
||||
* _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)
|
||||
* If == 200112L, you can use realtime scheduling.
|
||||
* This option indicates that the behaviour of some
|
||||
* implemented functions conforms to the additional TPS
|
||||
* requirements in the standard. E.g. rwlocks favour
|
||||
* writers over readers when threads have equal priority.
|
||||
*
|
||||
* _POSIX_THREAD_PRIO_INHERIT (== -1)
|
||||
* If == 200112L, you can create priority inheritance
|
||||
* mutexes.
|
||||
* pthread_mutexattr_getprotocol +
|
||||
* pthread_mutexattr_setprotocol +
|
||||
*
|
||||
* _POSIX_THREAD_PRIO_PROTECT (== -1)
|
||||
* If == 200112L, you can create priority ceiling mutexes
|
||||
* Indicates the availability of:
|
||||
* pthread_mutex_getprioceiling
|
||||
* pthread_mutex_setprioceiling
|
||||
* pthread_mutexattr_getprioceiling
|
||||
* pthread_mutexattr_getprotocol +
|
||||
* pthread_mutexattr_setprioceiling
|
||||
* pthread_mutexattr_setprotocol +
|
||||
*
|
||||
* _POSIX_THREAD_PROCESS_SHARED (== -1)
|
||||
* If set, you can create mutexes and condition
|
||||
* variables that can be shared with another
|
||||
* process.If set, indicates the availability
|
||||
* of:
|
||||
* pthread_mutexattr_getpshared
|
||||
* pthread_mutexattr_setpshared
|
||||
* pthread_condattr_getpshared
|
||||
* pthread_condattr_setpshared
|
||||
*
|
||||
* _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)
|
||||
* If == 200112L you can use the special *_r library
|
||||
* functions that provide thread-safe behaviour
|
||||
*
|
||||
* _POSIX_READER_WRITER_LOCKS (== 200112L)
|
||||
* If == 200112L, you can use read/write locks
|
||||
*
|
||||
* _POSIX_SPIN_LOCKS (== 200112L)
|
||||
* If == 200112L, you can use spin locks
|
||||
*
|
||||
* _POSIX_BARRIERS (== 200112L)
|
||||
* If == 200112L, you can use barriers
|
||||
*
|
||||
* + These functions provide both 'inherit' and/or
|
||||
* 'protect' protocol, based upon these macro
|
||||
* settings.
|
||||
*
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* POSIX Options
|
||||
*/
|
||||
#undef _POSIX_THREADS
|
||||
#define _POSIX_THREADS 200112L
|
||||
|
||||
#undef _POSIX_READER_WRITER_LOCKS
|
||||
#define _POSIX_READER_WRITER_LOCKS 200112L
|
||||
|
||||
#undef _POSIX_SPIN_LOCKS
|
||||
#define _POSIX_SPIN_LOCKS 200112L
|
||||
|
||||
#undef _POSIX_BARRIERS
|
||||
#define _POSIX_BARRIERS 200112L
|
||||
|
||||
#undef _POSIX_THREAD_SAFE_FUNCTIONS
|
||||
#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L
|
||||
|
||||
#undef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
#define _POSIX_THREAD_ATTR_STACKSIZE 200112L
|
||||
|
||||
/*
|
||||
* The following options are not supported
|
||||
*/
|
||||
#undef _POSIX_THREAD_ATTR_STACKADDR
|
||||
#define _POSIX_THREAD_ATTR_STACKADDR -1
|
||||
|
||||
#undef _POSIX_THREAD_PRIO_INHERIT
|
||||
#define _POSIX_THREAD_PRIO_INHERIT -1
|
||||
|
||||
#undef _POSIX_THREAD_PRIO_PROTECT
|
||||
#define _POSIX_THREAD_PRIO_PROTECT -1
|
||||
|
||||
/* TPS is not fully supported. */
|
||||
#undef _POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
#define _POSIX_THREAD_PRIORITY_SCHEDULING -1
|
||||
|
||||
#undef _POSIX_THREAD_PROCESS_SHARED
|
||||
#define _POSIX_THREAD_PROCESS_SHARED -1
|
||||
|
||||
|
||||
/*
|
||||
* POSIX 1003.1-2001 Limits
|
||||
* ===========================
|
||||
*
|
||||
* These limits are normally set in <limits.h>, which is not provided with
|
||||
* pthreads-embedded.
|
||||
*
|
||||
* PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
* Maximum number of attempts to destroy
|
||||
* a thread's thread-specific data on
|
||||
* termination (must be at least 4)
|
||||
*
|
||||
* PTHREAD_KEYS_MAX
|
||||
* Maximum number of thread-specific data keys
|
||||
* available per process (must be at least 128)
|
||||
*
|
||||
* PTHREAD_STACK_MIN
|
||||
* Minimum supported stack size for a thread
|
||||
*
|
||||
* PTHREAD_THREADS_MAX
|
||||
* Maximum number of threads supported per
|
||||
* process (must be at least 64).
|
||||
*
|
||||
* SEM_NSEMS_MAX
|
||||
* The maximum number of semaphores a process can have.
|
||||
* (must be at least 256)
|
||||
*
|
||||
* SEM_VALUE_MAX
|
||||
* The maximum value a semaphore can have.
|
||||
* (must be at least 32767)
|
||||
*
|
||||
*/
|
||||
#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4
|
||||
|
||||
#undef PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||
|
||||
#undef _POSIX_THREAD_KEYS_MAX
|
||||
#define _POSIX_THREAD_KEYS_MAX 128
|
||||
|
||||
#undef PTHREAD_KEYS_MAX
|
||||
#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX
|
||||
|
||||
#undef PTHREAD_STACK_MIN
|
||||
#define PTHREAD_STACK_MIN 0
|
||||
|
||||
#undef _POSIX_THREAD_THREADS_MAX
|
||||
#define _POSIX_THREAD_THREADS_MAX 64
|
||||
|
||||
/* Arbitrary value */
|
||||
#undef PTHREAD_THREADS_MAX
|
||||
#define PTHREAD_THREADS_MAX 2019
|
||||
|
||||
#undef _POSIX_SEM_NSEMS_MAX
|
||||
#define _POSIX_SEM_NSEMS_MAX 256
|
||||
|
||||
/* Arbitrary value */
|
||||
#undef SEM_NSEMS_MAX
|
||||
#define SEM_NSEMS_MAX 1024
|
||||
|
||||
#undef _POSIX_SEM_VALUE_MAX
|
||||
#define _POSIX_SEM_VALUE_MAX 32767
|
||||
|
||||
#undef SEM_VALUE_MAX
|
||||
#define SEM_VALUE_MAX INT_MAX
|
||||
|
||||
|
||||
/*
|
||||
* Generic handle type - intended to extend uniqueness beyond
|
||||
* that available with a simple pointer. It should scale for either
|
||||
* IA-32 or IA-64.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
void * p; /* Pointer to actual object */
|
||||
unsigned int x; /* Extra information - reuse count etc */
|
||||
} pte_handle_t;
|
||||
|
||||
typedef /* pte_handle_t */ void * pthread_t;
|
||||
typedef struct pthread_attr_t_ * pthread_attr_t;
|
||||
typedef struct pthread_once_t_ pthread_once_t;
|
||||
typedef struct pthread_key_t_ * pthread_key_t;
|
||||
typedef struct pthread_mutex_t_ * pthread_mutex_t;
|
||||
typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;
|
||||
typedef struct pthread_cond_t_ * pthread_cond_t;
|
||||
typedef struct pthread_condattr_t_ * pthread_condattr_t;
|
||||
typedef struct pthread_rwlock_t_ * pthread_rwlock_t;
|
||||
typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;
|
||||
typedef struct pthread_spinlock_t_ * pthread_spinlock_t;
|
||||
typedef struct pthread_barrier_t_ * pthread_barrier_t;
|
||||
typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* POSIX Threads
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
/*
|
||||
* pthread_attr_{get,set}detachstate
|
||||
*/
|
||||
PTHREAD_CREATE_JOINABLE = 0, /* Default */
|
||||
PTHREAD_CREATE_DETACHED = 1,
|
||||
|
||||
/*
|
||||
* pthread_attr_{get,set}inheritsched
|
||||
*/
|
||||
PTHREAD_INHERIT_SCHED = 0,
|
||||
PTHREAD_EXPLICIT_SCHED = 1, /* Default */
|
||||
|
||||
/*
|
||||
* pthread_{get,set}scope
|
||||
*/
|
||||
PTHREAD_SCOPE_PROCESS = 0,
|
||||
PTHREAD_SCOPE_SYSTEM = 1, /* Default */
|
||||
PTHREAD_SCOPE_PROCESS_VFPU = 2, /* PSP specific */
|
||||
|
||||
/*
|
||||
* pthread_setcancelstate paramters
|
||||
*/
|
||||
PTHREAD_CANCEL_ENABLE = 0, /* Default */
|
||||
PTHREAD_CANCEL_DISABLE = 1,
|
||||
|
||||
/*
|
||||
* pthread_setcanceltype parameters
|
||||
*/
|
||||
PTHREAD_CANCEL_ASYNCHRONOUS = 0,
|
||||
PTHREAD_CANCEL_DEFERRED = 1, /* Default */
|
||||
|
||||
/*
|
||||
* pthread_mutexattr_{get,set}pshared
|
||||
* pthread_condattr_{get,set}pshared
|
||||
*/
|
||||
PTHREAD_PROCESS_PRIVATE = 0,
|
||||
PTHREAD_PROCESS_SHARED = 1,
|
||||
|
||||
/*
|
||||
* pthread_barrier_wait
|
||||
*/
|
||||
PTHREAD_BARRIER_SERIAL_THREAD = -1
|
||||
};
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Cancelation
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
#define PTHREAD_CANCELED ((void *) -1)
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Once Key
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
#define PTHREAD_ONCE_INIT { PTE_FALSE, 0, 0, 0}
|
||||
|
||||
struct pthread_once_t_
|
||||
{
|
||||
int state;
|
||||
void * semaphore;
|
||||
int numSemaphoreUsers;
|
||||
int done; /* indicates if user function has been executed */
|
||||
// void * lock;
|
||||
// int reserved1;
|
||||
// int reserved2;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ====================
|
||||
* Object initialisers
|
||||
* ====================
|
||||
* ====================
|
||||
*/
|
||||
#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) -1)
|
||||
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t) -2)
|
||||
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t) -3)
|
||||
|
||||
/*
|
||||
* Compatibility with LinuxThreads
|
||||
*/
|
||||
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER
|
||||
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
|
||||
|
||||
#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) -1)
|
||||
|
||||
#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t) -1)
|
||||
|
||||
#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t) -1)
|
||||
|
||||
/*
|
||||
* Mutex types.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
/* Compatibility with LinuxThreads */
|
||||
PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_RECURSIVE_NP,
|
||||
PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||
PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,
|
||||
/* For compatibility with POSIX */
|
||||
PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,
|
||||
PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
|
||||
PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||
PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
|
||||
};
|
||||
|
||||
|
||||
typedef struct pte_cleanup_t pte_cleanup_t;
|
||||
|
||||
typedef void (* pte_cleanup_callback_t)(void *);
|
||||
|
||||
struct pte_cleanup_t
|
||||
{
|
||||
pte_cleanup_callback_t routine;
|
||||
void *arg;
|
||||
struct pte_cleanup_t *prev;
|
||||
};
|
||||
|
||||
/*
|
||||
* C implementation of PThreads cancel cleanup
|
||||
*/
|
||||
|
||||
#define pthread_cleanup_push( _rout, _arg ) \
|
||||
{ \
|
||||
pte_cleanup_t _cleanup; \
|
||||
\
|
||||
pte_push_cleanup( &_cleanup, (pte_cleanup_callback_t) (_rout), (_arg) ); \
|
||||
|
||||
#define pthread_cleanup_pop( _execute ) \
|
||||
(void) pte_pop_cleanup( _execute ); \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* ===============
|
||||
* ===============
|
||||
* Methods
|
||||
* ===============
|
||||
* ===============
|
||||
*/
|
||||
|
||||
int pthread_init (void);
|
||||
void pthread_terminate (void);
|
||||
|
||||
/*
|
||||
* PThread Attribute Functions
|
||||
*/
|
||||
int pthread_attr_init (pthread_attr_t * attr);
|
||||
|
||||
int pthread_attr_destroy (pthread_attr_t * attr);
|
||||
|
||||
int pthread_attr_getdetachstate (const pthread_attr_t * attr,
|
||||
int *detachstate);
|
||||
|
||||
int pthread_attr_getstackaddr (const pthread_attr_t * attr,
|
||||
void **stackaddr);
|
||||
|
||||
int pthread_attr_getstacksize (const pthread_attr_t * attr,
|
||||
size_t * stacksize);
|
||||
|
||||
int pthread_attr_setdetachstate (pthread_attr_t * attr,
|
||||
int detachstate);
|
||||
|
||||
int pthread_attr_setstackaddr (pthread_attr_t * attr,
|
||||
void *stackaddr);
|
||||
|
||||
int pthread_attr_setstacksize (pthread_attr_t * attr,
|
||||
size_t stacksize);
|
||||
|
||||
int pthread_attr_getschedparam (const pthread_attr_t *attr,
|
||||
struct sched_param *param);
|
||||
|
||||
int pthread_attr_setschedparam (pthread_attr_t *attr,
|
||||
const struct sched_param *param);
|
||||
|
||||
int pthread_attr_setschedpolicy (pthread_attr_t *,
|
||||
int);
|
||||
|
||||
int pthread_attr_getschedpolicy (pthread_attr_t *,
|
||||
int *);
|
||||
|
||||
int pthread_attr_setinheritsched(pthread_attr_t * attr,
|
||||
int inheritsched);
|
||||
|
||||
int pthread_attr_getinheritsched(pthread_attr_t * attr,
|
||||
int * inheritsched);
|
||||
|
||||
int pthread_attr_setscope (pthread_attr_t *,
|
||||
int);
|
||||
|
||||
int pthread_attr_getscope (const pthread_attr_t *,
|
||||
int *);
|
||||
|
||||
/*
|
||||
* PThread Functions
|
||||
*/
|
||||
int pthread_create (pthread_t * tid,
|
||||
const pthread_attr_t * attr,
|
||||
void *(*start) (void *),
|
||||
void *arg);
|
||||
|
||||
int pthread_detach (pthread_t tid);
|
||||
|
||||
int pthread_equal (pthread_t t1,
|
||||
pthread_t t2);
|
||||
|
||||
void pthread_exit (void *value_ptr);
|
||||
|
||||
int pthread_join (pthread_t thread,
|
||||
void **value_ptr);
|
||||
|
||||
pthread_t pthread_self (void);
|
||||
|
||||
int pthread_cancel (pthread_t thread);
|
||||
|
||||
int pthread_setcancelstate (int state,
|
||||
int *oldstate);
|
||||
|
||||
int pthread_setcanceltype (int type,
|
||||
int *oldtype);
|
||||
|
||||
void pthread_testcancel (void);
|
||||
|
||||
int pthread_once (pthread_once_t * once_control,
|
||||
void (*init_routine) (void));
|
||||
|
||||
#if PTE_LEVEL >= PTE_LEVEL_MAX
|
||||
pte_cleanup_t * pte_pop_cleanup (int execute);
|
||||
|
||||
void pte_push_cleanup (pte_cleanup_t * cleanup,
|
||||
void (*routine) (void *),
|
||||
void *arg);
|
||||
#endif /* PTE_LEVEL >= PTE_LEVEL_MAX */
|
||||
|
||||
/*
|
||||
* Thread Specific Data Functions
|
||||
*/
|
||||
int pthread_key_create (pthread_key_t * key,
|
||||
void (*destructor) (void *));
|
||||
|
||||
int pthread_key_delete (pthread_key_t key);
|
||||
|
||||
int pthread_setspecific (pthread_key_t key,
|
||||
const void *value);
|
||||
|
||||
void * pthread_getspecific (pthread_key_t key);
|
||||
|
||||
|
||||
/*
|
||||
* Mutex Attribute Functions
|
||||
*/
|
||||
int pthread_mutexattr_init (pthread_mutexattr_t * attr);
|
||||
|
||||
int pthread_mutexattr_destroy (pthread_mutexattr_t * attr);
|
||||
|
||||
int pthread_mutexattr_getpshared (const pthread_mutexattr_t
|
||||
* attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
int pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);
|
||||
int pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind);
|
||||
|
||||
/*
|
||||
* Barrier Attribute Functions
|
||||
*/
|
||||
int pthread_barrierattr_init (pthread_barrierattr_t * attr);
|
||||
|
||||
int pthread_barrierattr_destroy (pthread_barrierattr_t * attr);
|
||||
|
||||
int pthread_barrierattr_getpshared (const pthread_barrierattr_t
|
||||
* attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
/*
|
||||
* Mutex Functions
|
||||
*/
|
||||
int pthread_mutex_init (pthread_mutex_t * mutex,
|
||||
const pthread_mutexattr_t * attr);
|
||||
|
||||
int pthread_mutex_destroy (pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_mutex_lock (pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_mutex_timedlock(pthread_mutex_t *mutex,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_mutex_trylock (pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_mutex_unlock (pthread_mutex_t * mutex);
|
||||
|
||||
/*
|
||||
* Spinlock Functions
|
||||
*/
|
||||
int pthread_spin_init (pthread_spinlock_t * lock, int pshared);
|
||||
|
||||
int pthread_spin_destroy (pthread_spinlock_t * lock);
|
||||
|
||||
int pthread_spin_lock (pthread_spinlock_t * lock);
|
||||
|
||||
int pthread_spin_trylock (pthread_spinlock_t * lock);
|
||||
|
||||
int pthread_spin_unlock (pthread_spinlock_t * lock);
|
||||
|
||||
/*
|
||||
* Barrier Functions
|
||||
*/
|
||||
int pthread_barrier_init (pthread_barrier_t * barrier,
|
||||
const pthread_barrierattr_t * attr,
|
||||
unsigned int count);
|
||||
|
||||
int pthread_barrier_destroy (pthread_barrier_t * barrier);
|
||||
|
||||
int pthread_barrier_wait (pthread_barrier_t * barrier);
|
||||
|
||||
/*
|
||||
* Condition Variable Attribute Functions
|
||||
*/
|
||||
int pthread_condattr_init (pthread_condattr_t * attr);
|
||||
|
||||
int pthread_condattr_destroy (pthread_condattr_t * attr);
|
||||
|
||||
int pthread_condattr_getpshared (const pthread_condattr_t * attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_condattr_setpshared (pthread_condattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
/*
|
||||
* Condition Variable Functions
|
||||
*/
|
||||
int pthread_cond_init (pthread_cond_t * cond,
|
||||
const pthread_condattr_t * attr);
|
||||
|
||||
int pthread_cond_destroy (pthread_cond_t * cond);
|
||||
|
||||
int pthread_cond_wait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex);
|
||||
|
||||
int pthread_cond_timedwait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_cond_signal (pthread_cond_t * cond);
|
||||
|
||||
int pthread_cond_broadcast (pthread_cond_t * cond);
|
||||
|
||||
/*
|
||||
* Scheduling
|
||||
*/
|
||||
int pthread_setschedparam (pthread_t thread,
|
||||
int policy,
|
||||
const struct sched_param *param);
|
||||
|
||||
int pthread_getschedparam (pthread_t thread,
|
||||
int *policy,
|
||||
struct sched_param *param);
|
||||
|
||||
int pthread_setconcurrency (int);
|
||||
|
||||
int pthread_getconcurrency (void);
|
||||
|
||||
/*
|
||||
* Read-Write Lock Functions
|
||||
*/
|
||||
int pthread_rwlock_init(pthread_rwlock_t *lock,
|
||||
const pthread_rwlockattr_t *attr);
|
||||
|
||||
int pthread_rwlock_destroy(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
|
||||
|
||||
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
|
||||
|
||||
int pthread_rwlock_rdlock(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_rwlock_wrlock(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,
|
||||
const struct timespec *abstime);
|
||||
|
||||
int pthread_rwlock_unlock(pthread_rwlock_t *lock);
|
||||
|
||||
int pthread_rwlockattr_init (pthread_rwlockattr_t * attr);
|
||||
|
||||
int pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);
|
||||
|
||||
int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,
|
||||
int *pshared);
|
||||
|
||||
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,
|
||||
int pshared);
|
||||
|
||||
#if PTE_LEVEL >= PTE_LEVEL_MAX - 1
|
||||
|
||||
/*
|
||||
* Signal Functions. Should be defined in <signal.h> but we might
|
||||
* already have signal.h that don't define these.
|
||||
*/
|
||||
int pthread_kill(pthread_t thread, int sig);
|
||||
|
||||
/*
|
||||
* Non-portable functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Compatibility with Linux.
|
||||
*/
|
||||
int pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,
|
||||
int kind);
|
||||
int pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,
|
||||
int *kind);
|
||||
|
||||
/*
|
||||
* Possibly supported by other POSIX threads implementations
|
||||
*/
|
||||
int pthread_delay_np (struct timespec * interval);
|
||||
int pthread_num_processors_np(void);
|
||||
|
||||
/*
|
||||
* Register a system time change with the library.
|
||||
* Causes the library to perform various functions
|
||||
* in response to the change. Should be called whenever
|
||||
* the application's top level window receives a
|
||||
* WM_TIMECHANGE message. It can be passed directly to
|
||||
* pthread_create() as a new thread if desired.
|
||||
*/
|
||||
void * pthread_timechange_handler_np(void *);
|
||||
|
||||
#endif /*PTE_LEVEL >= PTE_LEVEL_MAX - 1 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* cplusplus */
|
||||
|
||||
#if PTE_LEVEL >= PTE_LEVEL_MAX
|
||||
|
||||
#endif /* PTE_LEVEL >= PTE_LEVEL_MAX */
|
||||
|
||||
/*
|
||||
* Some compiler environments don't define some things.
|
||||
*/
|
||||
# define _ftime ftime
|
||||
# define _timeb timeb
|
||||
|
||||
#undef PTE_LEVEL
|
||||
#undef PTE_LEVEL_MAX
|
||||
|
||||
#endif /* PTHREAD_H */
|
||||
Vendored
-581
@@ -1,581 +0,0 @@
|
||||
/*
|
||||
* pthread_attr.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements operations on thread attribute objects.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int
|
||||
pthread_attr_destroy (pthread_attr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a thread attributes object.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a thread attributes object.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect threads created with 'attr'.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully destroyed attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Set the attribute object to a specific invalid value.
|
||||
*/
|
||||
(*attr)->valid = 0;
|
||||
free (*attr);
|
||||
*attr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getdetachstate (const pthread_attr_t * attr, int *detachstate)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function determines whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* detachstate
|
||||
* pointer to an integer into which is returned one
|
||||
* of:
|
||||
*
|
||||
* PTHREAD_CREATE_JOINABLE
|
||||
* Thread ID is valid, must be joined
|
||||
*
|
||||
* PTHREAD_CREATE_DETACHED
|
||||
* Thread ID is invalid, cannot be joined,
|
||||
* canceled, or modified
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function determines whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) You cannot join or cancel detached threads.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved detach state,
|
||||
* EINVAL 'attr' is invalid
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || detachstate == NULL)
|
||||
{
|
||||
*detachstate = PTHREAD_CREATE_DETACHED;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*detachstate = (*attr)->detachstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getinheritsched (pthread_attr_t * attr, int *inheritsched)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || inheritsched == NULL)
|
||||
return EINVAL;
|
||||
|
||||
*inheritsched = (*attr)->inheritsched;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getschedparam (const pthread_attr_t * attr,
|
||||
struct sched_param *param)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || param == NULL)
|
||||
return EINVAL;
|
||||
|
||||
memcpy (param, &(*attr)->param, sizeof (*param));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getschedpolicy (pthread_attr_t * attr, int *policy)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0 || policy == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Validate the policy arg.
|
||||
* Check that a policy constant wasn't passed rather than &policy.
|
||||
*/
|
||||
if (policy <= (int *) SCHED_MAX)
|
||||
return EINVAL;
|
||||
|
||||
*policy = SCHED_OTHER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_getscope (const pthread_attr_t * attr, int *contentionscope)
|
||||
{
|
||||
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
*contentionscope = (*attr)->contentionscope;
|
||||
return 0;
|
||||
#else
|
||||
return ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pthread_attr_getstackaddr (const pthread_attr_t * attr, void **stackaddr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function determines the address of the stack
|
||||
* on which threads created with 'attr' will run.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stackaddr
|
||||
* pointer into which is returned the stack address.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function determines the address of the stack
|
||||
* on which threads created with 'attr' will run.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKADDR
|
||||
*
|
||||
* 2) Create only one thread for each stack
|
||||
* address..
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retreived stack address,
|
||||
* EINVAL 'attr' is invalid
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#if defined( _POSIX_THREAD_ATTR_STACKADDR )
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
*stackaddr = (*attr)->stackaddr;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
}
|
||||
|
||||
int
|
||||
pthread_attr_getstacksize (const pthread_attr_t * attr, size_t * stacksize)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function determines the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stacksize
|
||||
* pointer to size_t into which is returned the
|
||||
* stack size, in bytes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function determines the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKSIZE
|
||||
*
|
||||
* 2) Use on newly created attributes object to
|
||||
* find the default stack size.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved stack size,
|
||||
* EINVAL 'attr' is invalid
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
/* Everything is okay. */
|
||||
*stacksize = (*attr)->stacksize;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
}
|
||||
|
||||
int pthread_attr_init (pthread_attr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a thread attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a thread attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Used to define thread attributes
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_attr_t attr_result;
|
||||
|
||||
/* This is disallowed. */
|
||||
if (attr == NULL)
|
||||
return EINVAL;
|
||||
|
||||
attr_result = (pthread_attr_t) malloc (sizeof (*attr_result));
|
||||
|
||||
if (attr_result == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
/*
|
||||
* Default to zero size?
|
||||
*/
|
||||
attr_result->stacksize = 0;
|
||||
#endif
|
||||
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKADDR
|
||||
/* FIXME: Set this to something sensible when we support it. */
|
||||
attr_result->stackaddr = NULL;
|
||||
#endif
|
||||
|
||||
attr_result->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||
|
||||
/*
|
||||
* Win32 sets new threads to THREAD_PRIORITY_NORMAL and
|
||||
* not to that of the parent thread. We choose to default to
|
||||
* this arrangement.
|
||||
*/
|
||||
attr_result->param.sched_priority = pte_osThreadGetDefaultPriority();
|
||||
attr_result->inheritsched = PTHREAD_EXPLICIT_SCHED;
|
||||
attr_result->contentionscope = PTHREAD_SCOPE_SYSTEM;
|
||||
|
||||
attr_result->valid = PTE_ATTR_VALID;
|
||||
|
||||
*attr = attr_result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setdetachstate (pthread_attr_t * attr, int detachstate)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function specifies whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* detachstate
|
||||
* an integer containing one of:
|
||||
*
|
||||
* PTHREAD_CREATE_JOINABLE
|
||||
* Thread ID is valid, must be joined
|
||||
*
|
||||
* PTHREAD_CREATE_DETACHED
|
||||
* Thread ID is invalid, cannot be joined,
|
||||
* canceled, or modified
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function specifies whether threads created with
|
||||
* 'attr' will run detached.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) You cannot join or cancel detached threads.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set detach state,
|
||||
* EINVAL 'attr' or 'detachstate' is invalid
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (detachstate != PTHREAD_CREATE_JOINABLE &&
|
||||
detachstate != PTHREAD_CREATE_DETACHED)
|
||||
return EINVAL;
|
||||
|
||||
(*attr)->detachstate = detachstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setinheritsched (pthread_attr_t * attr, int inheritsched)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (PTHREAD_INHERIT_SCHED != inheritsched
|
||||
&& PTHREAD_EXPLICIT_SCHED != inheritsched)
|
||||
return EINVAL;
|
||||
|
||||
(*attr)->inheritsched = inheritsched;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setschedparam (pthread_attr_t * attr,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int priority;
|
||||
|
||||
if (pte_is_attr (attr) != 0 || param == NULL)
|
||||
return EINVAL;
|
||||
|
||||
priority = param->sched_priority;
|
||||
|
||||
/* Validate priority level. */
|
||||
if (priority < sched_get_priority_min (SCHED_OTHER) ||
|
||||
priority > sched_get_priority_max (SCHED_OTHER))
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
memcpy (&(*attr)->param, param, sizeof (*param));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setschedpolicy (pthread_attr_t * attr, int policy)
|
||||
{
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
if (policy != SCHED_OTHER)
|
||||
return ENOTSUP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_attr_setscope (pthread_attr_t * attr, int contentionscope)
|
||||
{
|
||||
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
|
||||
switch (contentionscope)
|
||||
{
|
||||
case PTHREAD_SCOPE_SYSTEM:
|
||||
(*attr)->contentionscope = contentionscope;
|
||||
return 0;
|
||||
case PTHREAD_SCOPE_PROCESS:
|
||||
return ENOTSUP;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
#else
|
||||
return ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pthread_attr_setstackaddr (pthread_attr_t * attr, void *stackaddr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Threads created with 'attr' will run on the stack
|
||||
* starting at 'stackaddr'.
|
||||
* Stack must be at least PTHREAD_STACK_MIN bytes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stacksize
|
||||
* stack size, in bytes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Threads created with 'attr' will run on the stack
|
||||
* starting at 'stackaddr'.
|
||||
* Stack must be at least PTHREAD_STACK_MIN bytes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKADDR
|
||||
*
|
||||
* 2) Create only one thread for each stack
|
||||
* address..
|
||||
*
|
||||
* 3) Ensure that stackaddr is aligned.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set stack address,
|
||||
* EINVAL 'attr' is invalid
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#if defined( _POSIX_THREAD_ATTR_STACKADDR )
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
(*attr)->stackaddr = stackaddr;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
}
|
||||
|
||||
int pthread_attr_setstacksize (pthread_attr_t * attr, size_t stacksize)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function specifies the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_attr_t
|
||||
*
|
||||
* stacksize
|
||||
* stack size, in bytes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function specifies the size of the stack on
|
||||
* which threads created with 'attr' will run.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Function supported only if this macro is
|
||||
* defined:
|
||||
*
|
||||
* _POSIX_THREAD_ATTR_STACKSIZE
|
||||
*
|
||||
* 2) Find the default first (using
|
||||
* pthread_attr_getstacksize), then increase
|
||||
* by multiplying.
|
||||
*
|
||||
* 3) Only use if thread needs more than the
|
||||
* default.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set stack size,
|
||||
* EINVAL 'attr' is invalid or stacksize too
|
||||
* small or too big.
|
||||
* ENOSYS function not supported
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
||||
|
||||
#if PTHREAD_STACK_MIN > 0
|
||||
|
||||
/* Verify that the stack size is within range. */
|
||||
if (stacksize < PTHREAD_STACK_MIN)
|
||||
return EINVAL;
|
||||
|
||||
#endif
|
||||
|
||||
if (pte_is_attr (attr) != 0)
|
||||
return EINVAL;
|
||||
|
||||
/* Everything is okay. */
|
||||
(*attr)->stacksize = stacksize;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
}
|
||||
Vendored
-163
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* pthread_barrier.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements barrier primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int pthread_barrier_destroy (pthread_barrier_t * barrier)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_barrier_t b;
|
||||
|
||||
if (barrier == NULL || *barrier == (pthread_barrier_t) PTE_OBJECT_INVALID)
|
||||
return EINVAL;
|
||||
|
||||
b = *barrier;
|
||||
*barrier = NULL;
|
||||
|
||||
if (0 == (result = sem_destroy (&(b->semBarrierBreeched[0]))))
|
||||
{
|
||||
if (0 == (result = sem_destroy (&(b->semBarrierBreeched[1]))))
|
||||
{
|
||||
(void) free (b);
|
||||
return 0;
|
||||
}
|
||||
(void) sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0);
|
||||
}
|
||||
|
||||
*barrier = b;
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_barrier_init (pthread_barrier_t * barrier,
|
||||
const pthread_barrierattr_t * attr, unsigned int count)
|
||||
{
|
||||
pthread_barrier_t b;
|
||||
|
||||
if (barrier == NULL || count == 0)
|
||||
return EINVAL;
|
||||
|
||||
if (NULL != (b = (pthread_barrier_t) calloc (1, sizeof (*b))))
|
||||
{
|
||||
b->pshared = (attr != NULL && *attr != NULL
|
||||
? (*attr)->pshared : PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count;
|
||||
b->iStep = 0;
|
||||
|
||||
/*
|
||||
* Two semaphores are used in the same way as two stepping
|
||||
* stones might be used in crossing a stream. Once all
|
||||
* threads are safely on one stone, the other stone can
|
||||
* be moved ahead, and the threads can start moving to it.
|
||||
* If some threads decide to eat their lunch before moving
|
||||
* then the other threads have to wait.
|
||||
*/
|
||||
if (0 == sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0))
|
||||
{
|
||||
if (0 == sem_init (&(b->semBarrierBreeched[1]), b->pshared, 0))
|
||||
{
|
||||
*barrier = b;
|
||||
return 0;
|
||||
}
|
||||
(void) sem_destroy (&(b->semBarrierBreeched[0]));
|
||||
}
|
||||
(void) free (b);
|
||||
}
|
||||
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
int pthread_barrier_wait (pthread_barrier_t * barrier)
|
||||
{
|
||||
int result;
|
||||
int step;
|
||||
pthread_barrier_t b;
|
||||
|
||||
if (barrier == NULL || *barrier == (pthread_barrier_t) PTE_OBJECT_INVALID)
|
||||
return EINVAL;
|
||||
|
||||
b = *barrier;
|
||||
step = b->iStep;
|
||||
|
||||
if (0 == PTE_ATOMIC_DECREMENT ((int *) &(b->nCurrentBarrierHeight)))
|
||||
{
|
||||
/* Must be done before posting the semaphore. */
|
||||
b->nCurrentBarrierHeight = b->nInitialBarrierHeight;
|
||||
|
||||
/*
|
||||
* There is no race condition between the semaphore wait and post
|
||||
* because we are using two alternating semas and all threads have
|
||||
* entered barrier_wait and checked nCurrentBarrierHeight before this
|
||||
* barrier's sema can be posted. Any threads that have not quite
|
||||
* entered sem_wait below when the multiple_post has completed
|
||||
* will nevertheless continue through the semaphore (barrier)
|
||||
* and will not be left stranded.
|
||||
*/
|
||||
result = (b->nInitialBarrierHeight > 1
|
||||
? sem_post_multiple (&(b->semBarrierBreeched[step]),
|
||||
b->nInitialBarrierHeight - 1) : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Use the non-cancelable version of sem_wait().
|
||||
*/
|
||||
result = sem_wait (&(b->semBarrierBreeched[step]));
|
||||
// result = sem_wait_nocancel (&(b->semBarrierBreeched[step]));
|
||||
}
|
||||
|
||||
/*
|
||||
* The first thread across will be the PTHREAD_BARRIER_SERIAL_THREAD.
|
||||
* This also sets up the alternate semaphore as the next barrier.
|
||||
*/
|
||||
if (0 == result)
|
||||
{
|
||||
result = (step ==
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (& (b->iStep),(1L - step),step) ?
|
||||
PTHREAD_BARRIER_SERIAL_THREAD : 0);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-257
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* pthread_barrier_attr.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements barrier primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_barrierattr_destroy (pthread_barrierattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a barrier attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a barrier attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect barrieres created using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
pthread_barrierattr_t ba = *attr;
|
||||
|
||||
*attr = NULL;
|
||||
free (ba);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_barrierattr_getpshared (const pthread_barrierattr_t * attr,
|
||||
int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether barriers created with 'attr' can be
|
||||
* shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_barrier_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared barriers MUST be allocated in shared
|
||||
* memory.
|
||||
* 2) The following macro is defined if shared barriers
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_barrierattr_init (pthread_barrierattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a barrier attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a barrier attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Used to define barrier types
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_barrierattr_t ba = (pthread_barrierattr_t) calloc (1, sizeof (*ba));
|
||||
|
||||
if (ba == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
ba->pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
*attr = ba;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Barriers created with 'attr' can be shared between
|
||||
* processes if pthread_barrier_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_barrierattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_barrier_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared barriers MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared barriers
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((pshared == PTHREAD_PROCESS_SHARED) ||
|
||||
(pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
#else
|
||||
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
|
||||
} /* pthread_barrierattr_setpshared */
|
||||
Vendored
-807
@@ -1,807 +0,0 @@
|
||||
/*
|
||||
* pthread_cond.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements condition variables and their primitives.
|
||||
*
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
/*
|
||||
* Arguments for cond_wait_cleanup, since we can only pass a
|
||||
* single void * to it.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t *mutexPtr;
|
||||
pthread_cond_t cv;
|
||||
int *resultPtr;
|
||||
} pte_cond_wait_cleanup_args_t;
|
||||
|
||||
static void pte_cond_wait_cleanup (void *args)
|
||||
{
|
||||
pte_cond_wait_cleanup_args_t *cleanup_args =
|
||||
(pte_cond_wait_cleanup_args_t *) args;
|
||||
pthread_cond_t cv = cleanup_args->cv;
|
||||
int *resultPtr = cleanup_args->resultPtr;
|
||||
int nSignalsWasLeft;
|
||||
int result;
|
||||
|
||||
/*
|
||||
* Whether we got here as a result of signal/broadcast or because of
|
||||
* timeout on wait or thread cancellation we indicate that we are no
|
||||
* longer waiting. The waiter is responsible for adjusting waiters
|
||||
* (to)unblock(ed) counts (protected by unblock lock).
|
||||
*/
|
||||
if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
|
||||
{
|
||||
*resultPtr = result;
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock))
|
||||
{
|
||||
--(cv->nWaitersToUnblock);
|
||||
}
|
||||
else if (INT_MAX / 2 == ++(cv->nWaitersGone))
|
||||
{
|
||||
/* Use the non-cancellable version of sem_wait() */
|
||||
// if (sem_wait_nocancel (&(cv->semBlockLock)) != 0)
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
*resultPtr = errno;
|
||||
/*
|
||||
* This is a fatal error for this CV,
|
||||
* so we deliberately don't unlock
|
||||
* cv->mtxUnblockLock before returning.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
cv->nWaitersBlocked -= cv->nWaitersGone;
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
*resultPtr = errno;
|
||||
/*
|
||||
* This is a fatal error for this CV,
|
||||
* so we deliberately don't unlock
|
||||
* cv->mtxUnblockLock before returning.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
cv->nWaitersGone = 0;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0)
|
||||
{
|
||||
*resultPtr = result;
|
||||
return;
|
||||
}
|
||||
|
||||
if (1 == nSignalsWasLeft)
|
||||
{
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
*resultPtr = errno;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* XSH: Upon successful return, the mutex has been locked and is owned
|
||||
* by the calling thread.
|
||||
*/
|
||||
if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0)
|
||||
*resultPtr = result;
|
||||
}
|
||||
|
||||
static int pte_cond_timedwait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex, const struct timespec *abstime)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_cond_t cv;
|
||||
pte_cond_wait_cleanup_args_t cleanup_args;
|
||||
|
||||
if (cond == NULL || *cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static condition variable. We check
|
||||
* again inside the guarded section of pte_cond_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*cond == PTHREAD_COND_INITIALIZER)
|
||||
result = pte_cond_check_need_init (cond);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
return result;
|
||||
|
||||
cv = *cond;
|
||||
|
||||
/* Thread can be cancelled in sem_wait() but this is OK */
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
return errno;
|
||||
|
||||
++(cv->nWaitersBlocked);
|
||||
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
return errno;
|
||||
|
||||
/*
|
||||
* Setup this waiter cleanup handler
|
||||
*/
|
||||
cleanup_args.mutexPtr = mutex;
|
||||
cleanup_args.cv = cv;
|
||||
cleanup_args.resultPtr = &result;
|
||||
|
||||
pthread_cleanup_push (pte_cond_wait_cleanup, (void *) &cleanup_args);
|
||||
|
||||
/*
|
||||
* Now we can release 'mutex' and...
|
||||
*/
|
||||
if ((result = pthread_mutex_unlock (mutex)) == 0)
|
||||
{
|
||||
/*
|
||||
* ...wait to be awakened by
|
||||
* pthread_cond_signal, or
|
||||
* pthread_cond_broadcast, or
|
||||
* timeout, or
|
||||
* thread cancellation
|
||||
*
|
||||
* Note:
|
||||
*
|
||||
* sem_timedwait is a cancellation point,
|
||||
* hence providing the mechanism for making
|
||||
* pthread_cond_wait a cancellation point.
|
||||
* We use the cleanup mechanism to ensure we
|
||||
* re-lock the mutex and adjust (to)unblock(ed) waiters
|
||||
* counts if we are cancelled, timed out or signalled.
|
||||
*/
|
||||
if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
|
||||
result = errno;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Always cleanup
|
||||
*/
|
||||
pthread_cleanup_pop (1);
|
||||
|
||||
/*
|
||||
* "result" can be modified by the cleanup handler.
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
pthread_cond_destroy (pthread_cond_t * cond)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function destroys a condition variable
|
||||
*
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function destroys a condition variable.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) A condition variable can be destroyed
|
||||
* immediately after all the threads that
|
||||
* are blocked on it are awakened. e.g.
|
||||
*
|
||||
* struct list {
|
||||
* pthread_mutex_t lm;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* struct elt {
|
||||
* key k;
|
||||
* int busy;
|
||||
* pthread_cond_t notbusy;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
*
|
||||
* struct elt *
|
||||
* list_find(struct list *lp, key k)
|
||||
* {
|
||||
* struct elt *ep;
|
||||
*
|
||||
* pthread_mutex_lock(&lp->lm);
|
||||
* while ((ep = find_elt(l,k) != NULL) && ep->busy)
|
||||
* pthread_cond_wait(&ep->notbusy, &lp->lm);
|
||||
* if (ep != NULL)
|
||||
* ep->busy = 1;
|
||||
* pthread_mutex_unlock(&lp->lm);
|
||||
* return(ep);
|
||||
* }
|
||||
*
|
||||
* delete_elt(struct list *lp, struct elt *ep)
|
||||
* {
|
||||
* pthread_mutex_lock(&lp->lm);
|
||||
* assert(ep->busy);
|
||||
* ... remove ep from list ...
|
||||
* ep->busy = 0;
|
||||
* (A) pthread_cond_broadcast(&ep->notbusy);
|
||||
* pthread_mutex_unlock(&lp->lm);
|
||||
* (B) pthread_cond_destroy(&rp->notbusy);
|
||||
* free(ep);
|
||||
* }
|
||||
*
|
||||
* In this example, the condition variable
|
||||
* and its list element may be freed (line B)
|
||||
* immediately after all threads waiting for
|
||||
* it are awakened (line A), since the mutex
|
||||
* and the code ensure that no other thread
|
||||
* can touch the element to be deleted.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released condition variable,
|
||||
* EINVAL 'cond' is invalid,
|
||||
* EBUSY 'cond' is in use,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_cond_t cv;
|
||||
int result = 0, result1 = 0, result2 = 0;
|
||||
|
||||
/*
|
||||
* Assuming any race condition here is harmless.
|
||||
*/
|
||||
if (cond == NULL || *cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (*cond != PTHREAD_COND_INITIALIZER)
|
||||
{
|
||||
|
||||
pte_osMutexLock (pte_cond_list_lock);
|
||||
|
||||
cv = *cond;
|
||||
|
||||
/*
|
||||
* Close the gate; this will synchronize this thread with
|
||||
* all already signaled waiters to let them retract their
|
||||
* waiter status - SEE NOTE 1 ABOVE!!!
|
||||
*/
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
return errno;
|
||||
|
||||
/*
|
||||
* !TRY! lock mtxUnblockLock; try will detect busy condition
|
||||
* and will not cause a deadlock with respect to concurrent
|
||||
* signal/broadcast.
|
||||
*/
|
||||
if ((result = pthread_mutex_trylock (&(cv->mtxUnblockLock))) != 0)
|
||||
{
|
||||
(void) sem_post (&(cv->semBlockLock));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether cv is still busy (still has waiters)
|
||||
*/
|
||||
if (cv->nWaitersBlocked > cv->nWaitersGone)
|
||||
{
|
||||
if (sem_post (&(cv->semBlockLock)) != 0)
|
||||
result = errno;
|
||||
result1 = pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
result2 = EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Now it is safe to destroy
|
||||
*/
|
||||
*cond = NULL;
|
||||
|
||||
if (sem_destroy (&(cv->semBlockLock)) != 0)
|
||||
result = errno;
|
||||
if (sem_destroy (&(cv->semBlockQueue)) != 0)
|
||||
result1 = errno;
|
||||
if ((result2 = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0)
|
||||
result2 = pthread_mutex_destroy (&(cv->mtxUnblockLock));
|
||||
|
||||
/* Unlink the CV from the list */
|
||||
|
||||
if (pte_cond_list_head == cv)
|
||||
pte_cond_list_head = cv->next;
|
||||
else
|
||||
cv->prev->next = cv->next;
|
||||
|
||||
if (pte_cond_list_tail == cv)
|
||||
pte_cond_list_tail = cv->prev;
|
||||
else
|
||||
cv->next->prev = cv->prev;
|
||||
|
||||
(void) free (cv);
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_cond_list_lock);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_cond_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_cond_test_init_lock);
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*cond == PTHREAD_COND_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised cond that has not yet been used (initialised).
|
||||
* If we get to here, another thread waiting to initialise
|
||||
* this cond will get an EINVAL. That's OK.
|
||||
*/
|
||||
*cond = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The cv has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_cond_test_init_lock);
|
||||
}
|
||||
|
||||
return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));
|
||||
}
|
||||
|
||||
int pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function initializes a condition variable.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
* attr
|
||||
* specifies optional creation attributes.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function initializes a condition variable.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created condition variable,
|
||||
* EINVAL 'attr' is invalid,
|
||||
* EAGAIN insufficient resources (other than
|
||||
* memory,
|
||||
* ENOMEM insufficient memory,
|
||||
* EBUSY 'cond' is already initialized,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
pthread_cond_t cv = NULL;
|
||||
|
||||
if (cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((*attr)->pshared == PTHREAD_PROCESS_SHARED))
|
||||
{
|
||||
/*
|
||||
* Creating condition variable that can be shared between
|
||||
* processes.
|
||||
*/
|
||||
result = ENOSYS;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
cv = (pthread_cond_t) calloc (1, sizeof (*cv));
|
||||
|
||||
if (cv == NULL)
|
||||
{
|
||||
result = ENOMEM;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
cv->nWaitersBlocked = 0;
|
||||
cv->nWaitersToUnblock = 0;
|
||||
cv->nWaitersGone = 0;
|
||||
|
||||
if (sem_init (&(cv->semBlockLock), 0, 1) != 0)
|
||||
{
|
||||
result = errno;
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
if (sem_init (&(cv->semBlockQueue), 0, 0) != 0)
|
||||
{
|
||||
result = errno;
|
||||
goto FAIL1;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_init (&(cv->mtxUnblockLock), 0)) != 0)
|
||||
{
|
||||
goto FAIL2;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
goto DONE;
|
||||
|
||||
/*
|
||||
* -------------
|
||||
* Failed...
|
||||
* -------------
|
||||
*/
|
||||
FAIL2:
|
||||
(void) sem_destroy (&(cv->semBlockQueue));
|
||||
|
||||
FAIL1:
|
||||
(void) sem_destroy (&(cv->semBlockLock));
|
||||
|
||||
FAIL0:
|
||||
(void) free (cv);
|
||||
cv = NULL;
|
||||
|
||||
DONE:
|
||||
if (0 == result)
|
||||
{
|
||||
|
||||
pte_osMutexLock (pte_cond_list_lock);
|
||||
|
||||
cv->next = NULL;
|
||||
cv->prev = pte_cond_list_tail;
|
||||
|
||||
if (pte_cond_list_tail != NULL)
|
||||
pte_cond_list_tail->next = cv;
|
||||
|
||||
pte_cond_list_tail = cv;
|
||||
|
||||
if (pte_cond_list_head == NULL)
|
||||
pte_cond_list_head = cv;
|
||||
|
||||
pte_osMutexUnlock(pte_cond_list_lock);
|
||||
}
|
||||
|
||||
*cond = cv;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int pte_cond_unblock (pthread_cond_t * cond, int unblockAll)
|
||||
/*
|
||||
* Notes.
|
||||
*
|
||||
* Does not use the external mutex for synchronisation,
|
||||
* therefore semBlockLock is needed.
|
||||
* mtxUnblockLock is for LEVEL-2 synch. LEVEL-2 is the
|
||||
* state where the external mutex is not necessarily locked by
|
||||
* any thread, ie. between cond_wait unlocking and re-acquiring
|
||||
* the lock after having been signaled or a timeout or
|
||||
* cancellation.
|
||||
*
|
||||
* Uses the following CV elements:
|
||||
* nWaitersBlocked
|
||||
* nWaitersToUnblock
|
||||
* nWaitersGone
|
||||
* mtxUnblockLock
|
||||
* semBlockLock
|
||||
* semBlockQueue
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
pthread_cond_t cv;
|
||||
int nSignalsToIssue;
|
||||
|
||||
if (cond == NULL || *cond == NULL)
|
||||
return EINVAL;
|
||||
|
||||
cv = *cond;
|
||||
|
||||
/*
|
||||
* No-op if the CV is static and hasn't been initialised yet.
|
||||
* Assuming that any race condition is harmless.
|
||||
*/
|
||||
if (cv == PTHREAD_COND_INITIALIZER)
|
||||
return 0;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
|
||||
return result;
|
||||
|
||||
if (0 != cv->nWaitersToUnblock)
|
||||
{
|
||||
if (0 == cv->nWaitersBlocked)
|
||||
return pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
|
||||
if (unblockAll)
|
||||
{
|
||||
cv->nWaitersToUnblock += (nSignalsToIssue = cv->nWaitersBlocked);
|
||||
cv->nWaitersBlocked = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nSignalsToIssue = 1;
|
||||
cv->nWaitersToUnblock++;
|
||||
cv->nWaitersBlocked--;
|
||||
}
|
||||
}
|
||||
else if (cv->nWaitersBlocked > cv->nWaitersGone)
|
||||
{
|
||||
/* Use the non-cancellable version of sem_wait() */
|
||||
// if (sem_wait_nocancel (&(cv->semBlockLock)) != 0)
|
||||
if (sem_wait (&(cv->semBlockLock)) != 0)
|
||||
{
|
||||
result = errno;
|
||||
(void) pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
return result;
|
||||
}
|
||||
if (0 != cv->nWaitersGone)
|
||||
{
|
||||
cv->nWaitersBlocked -= cv->nWaitersGone;
|
||||
cv->nWaitersGone = 0;
|
||||
}
|
||||
if (unblockAll)
|
||||
{
|
||||
nSignalsToIssue = cv->nWaitersToUnblock = cv->nWaitersBlocked;
|
||||
cv->nWaitersBlocked = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nSignalsToIssue = cv->nWaitersToUnblock = 1;
|
||||
cv->nWaitersBlocked--;
|
||||
}
|
||||
}
|
||||
else
|
||||
return pthread_mutex_unlock (&(cv->mtxUnblockLock));
|
||||
|
||||
if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0)
|
||||
{
|
||||
if (sem_post_multiple (&(cv->semBlockQueue), nSignalsToIssue) != 0)
|
||||
result = errno;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_cond_signal (pthread_cond_t * cond)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function signals a condition variable, waking
|
||||
* one waiting thread.
|
||||
* If SCHED_FIFO or SCHED_RR policy threads are waiting
|
||||
* the highest priority waiter is awakened; otherwise,
|
||||
* an unspecified waiter is awakened.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function signals a condition variable, waking
|
||||
* one waiting thread.
|
||||
* If SCHED_FIFO or SCHED_RR policy threads are waiting
|
||||
* the highest priority waiter is awakened; otherwise,
|
||||
* an unspecified waiter is awakened.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1) Use when any waiter can respond and only one need
|
||||
* respond (all waiters being equal).
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully signaled condition,
|
||||
* EINVAL 'cond' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* The '0'(FALSE) unblockAll arg means unblock ONE waiter.
|
||||
*/
|
||||
return (pte_cond_unblock (cond, 0));
|
||||
|
||||
} /* pthread_cond_signal */
|
||||
|
||||
int
|
||||
pthread_cond_broadcast (pthread_cond_t * cond)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function broadcasts the condition variable,
|
||||
* waking all current waiters.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function signals a condition variable, waking
|
||||
* all waiting threads.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1) Use when more than one waiter may respond to
|
||||
* predicate change or if any waiting thread may
|
||||
* not be able to respond
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully signalled condition to all
|
||||
* waiting threads,
|
||||
* EINVAL 'cond' is invalid
|
||||
* ENOSPC a required resource has been exhausted,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* The TRUE unblockAll arg means unblock ALL waiters.
|
||||
*/
|
||||
return (pte_cond_unblock (cond, PTE_TRUE));
|
||||
}
|
||||
|
||||
int pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a condition variable until
|
||||
* awakened by a signal or broadcast.
|
||||
*
|
||||
* Caller MUST be holding the mutex lock; the
|
||||
* lock is released and the caller is blocked waiting
|
||||
* on 'cond'. When 'cond' is signaled, the mutex
|
||||
* is re-acquired before returning to the caller.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
* mutex
|
||||
* pointer to an instance of pthread_mutex_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a condition variable until
|
||||
* awakened by a signal or broadcast.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* 1) The function must be called with 'mutex' LOCKED
|
||||
* by the calling thread, or undefined behaviour
|
||||
* will result.
|
||||
*
|
||||
* 2) This routine atomically releases 'mutex' and causes
|
||||
* the calling thread to block on the condition variable.
|
||||
* The blocked thread may be awakened by
|
||||
* pthread_cond_signal or
|
||||
* pthread_cond_broadcast.
|
||||
*
|
||||
* Upon successful completion, the 'mutex' has been locked and
|
||||
* is owned by the calling thread.
|
||||
*
|
||||
*
|
||||
* RESULTS
|
||||
* 0 caught condition; mutex released,
|
||||
* EINVAL 'cond' or 'mutex' is invalid,
|
||||
* EINVAL different mutexes for concurrent waits,
|
||||
* EINVAL mutex is not held by the calling thread,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* The NULL abstime arg means INFINITE waiting.
|
||||
*/
|
||||
return (pte_cond_timedwait (cond, mutex, NULL));
|
||||
|
||||
} /* pthread_cond_wait */
|
||||
|
||||
|
||||
int
|
||||
pthread_cond_timedwait (pthread_cond_t * cond,
|
||||
pthread_mutex_t * mutex,
|
||||
const struct timespec *abstime)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a condition variable either until
|
||||
* awakened by a signal or broadcast; or until the time
|
||||
* specified by abstime passes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* cond
|
||||
* pointer to an instance of pthread_cond_t
|
||||
*
|
||||
* mutex
|
||||
* pointer to an instance of pthread_mutex_t
|
||||
*
|
||||
* abstime
|
||||
* pointer to an instance of (const struct timespec)
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a condition variable either until
|
||||
* awakened by a signal or broadcast; or until the time
|
||||
* specified by abstime passes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) The function must be called with 'mutex' LOCKED
|
||||
* by the calling thread, or undefined behaviour
|
||||
* will result.
|
||||
*
|
||||
* 2) This routine atomically releases 'mutex' and causes
|
||||
* the calling thread to block on the condition variable.
|
||||
* The blocked thread may be awakened by
|
||||
* pthread_cond_signal or
|
||||
* pthread_cond_broadcast.
|
||||
*
|
||||
*
|
||||
* RESULTS
|
||||
* 0 caught condition; mutex released,
|
||||
* EINVAL 'cond', 'mutex', or abstime is invalid,
|
||||
* EINVAL different mutexes for concurrent waits,
|
||||
* EINVAL mutex is not held by the calling thread,
|
||||
* ETIMEDOUT abstime ellapsed before cond was signaled.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (abstime == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return (pte_cond_timedwait (cond, mutex, abstime));
|
||||
}
|
||||
Vendored
-259
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* condvar_attr_destroy.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements condition variables and their primitives.
|
||||
*
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_condattr_destroy (pthread_condattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a condition variable attributes object.
|
||||
* The object can no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_condattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a condition variable attributes object.
|
||||
* The object can no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect condition variables created
|
||||
* using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
(void) free (*attr);
|
||||
|
||||
*attr = NULL;
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether condition variables created with 'attr'
|
||||
* can be shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_condattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Condition Variables created with 'attr' can be shared
|
||||
* between processes if pthread_cond_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared condition variables MUST be allocated in
|
||||
* shared memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' or 'pshared' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_condattr_init (pthread_condattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a condition variable attributes object
|
||||
* with default attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_condattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a condition variable attributes object
|
||||
* with default attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Use to define condition variable types
|
||||
* 2) It is up to the application to ensure
|
||||
* that it doesn't re-init an attribute
|
||||
* without destroying it first. Otherwise
|
||||
* a memory leak is created.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_condattr_t attr_result;
|
||||
int result = 0;
|
||||
|
||||
attr_result = (pthread_condattr_t) calloc (1, sizeof (*attr_result));
|
||||
|
||||
if (attr_result == NULL)
|
||||
result = ENOMEM;
|
||||
|
||||
*attr = attr_result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Mutexes created with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared mutexes MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL)
|
||||
&& ((pshared == PTHREAD_PROCESS_SHARED)
|
||||
|| (pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
#else
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
Vendored
-111
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* pthread_get.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements miscellaneous thread functions.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int pthread_getconcurrency (void)
|
||||
{
|
||||
return pte_concurrency;
|
||||
}
|
||||
|
||||
int pthread_getschedparam (pthread_t thread, int *policy,
|
||||
struct sched_param *param)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Validate the thread id. */
|
||||
result = pthread_kill (thread, 0);
|
||||
if (0 != result)
|
||||
return result;
|
||||
|
||||
/*
|
||||
* Validate the policy and param args.
|
||||
* Check that a policy constant wasn't passed rather than &policy.
|
||||
*/
|
||||
if (policy <= (int *) SCHED_MAX || param == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/* Fill out the policy. */
|
||||
*policy = SCHED_OTHER;
|
||||
|
||||
/*
|
||||
* This function must return the priority value set by
|
||||
* the most recent pthread_setschedparam() or pthread_create()
|
||||
* for the target thread. It must not return the actual thread
|
||||
* priority as altered by any system priority adjustments etc.
|
||||
*/
|
||||
param->sched_priority = ((pte_thread_t *)thread)->sched_priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *pthread_getspecific (pthread_key_t key)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function returns the current value of key in the
|
||||
* calling thread. If no value has been set for 'key' in
|
||||
* the thread, NULL is returned.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* an instance of pthread_key_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function returns the current value of key in the
|
||||
* calling thread. If no value has been set for 'key' in
|
||||
* the thread, NULL is returned.
|
||||
*
|
||||
* RESULTS
|
||||
* key value or NULL on failure
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
return pte_osTlsGetValue (key->key);
|
||||
}
|
||||
Vendored
-204
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* pthread_key.c
|
||||
*
|
||||
* Description:
|
||||
* POSIX thread functions which implement thread-specific data (TSD).
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_key_create (pthread_key_t * key, void (*destructor) (void *))
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function creates a thread-specific data key visible
|
||||
* to all threads. All existing and new threads have a value
|
||||
* NULL for key until set using pthread_setspecific. When any
|
||||
* thread with a non-NULL value for key terminates, 'destructor'
|
||||
* is called with key's current value for that thread.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* pointer to an instance of pthread_key_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function creates a thread-specific data key visible
|
||||
* to all threads. All existing and new threads have a value
|
||||
* NULL for key until set using pthread_setspecific. When any
|
||||
* thread with a non-NULL value for key terminates, 'destructor'
|
||||
* is called with key's current value for that thread.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created semaphore,
|
||||
* EAGAIN insufficient resources or PTHREAD_KEYS_MAX
|
||||
* exceeded,
|
||||
* ENOMEM insufficient memory to create the key,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_key_t newkey;
|
||||
|
||||
if ((newkey = (pthread_key_t) calloc (1, sizeof (*newkey))) == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
{
|
||||
pte_osResult osResult = pte_osTlsAlloc(&(newkey->key));
|
||||
|
||||
if (osResult != PTE_OS_OK)
|
||||
{
|
||||
result = EAGAIN;
|
||||
|
||||
free (newkey);
|
||||
newkey = NULL;
|
||||
}
|
||||
else if (destructor != NULL)
|
||||
{
|
||||
/*
|
||||
* Have to manage associations between thread and key;
|
||||
* Therefore, need a lock that allows multiple threads
|
||||
* to gain exclusive access to the key->threads list.
|
||||
*
|
||||
* The mutex will only be created when it is first locked.
|
||||
*/
|
||||
newkey->keyLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
newkey->destructor = destructor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*key = newkey;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_key_delete (pthread_key_t key)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function deletes a thread-specific data key. This
|
||||
* does not change the value of the thread specific data key
|
||||
* for any thread and does not run the key's destructor
|
||||
* in any thread so it should be used with caution.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* pointer to an instance of pthread_key_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function deletes a thread-specific data key. This
|
||||
* does not change the value of the thread specific data key
|
||||
* for any thread and does not run the key's destructor
|
||||
* in any thread so it should be used with caution.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully deleted the key,
|
||||
* EINVAL key is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (key != NULL)
|
||||
{
|
||||
if (key->threads != NULL &&
|
||||
key->destructor != NULL &&
|
||||
pthread_mutex_lock (&(key->keyLock)) == 0)
|
||||
{
|
||||
ThreadKeyAssoc *assoc;
|
||||
/*
|
||||
* Run through all Thread<-->Key associations
|
||||
* for this key.
|
||||
*
|
||||
* While we hold at least one of the locks guarding
|
||||
* the assoc, we know that the assoc pointed to by
|
||||
* key->threads is valid.
|
||||
*/
|
||||
while ((assoc = (ThreadKeyAssoc *) key->threads) != NULL)
|
||||
{
|
||||
pte_thread_t * thread = assoc->thread;
|
||||
|
||||
/* Finished */
|
||||
if (assoc == NULL)
|
||||
break;
|
||||
|
||||
if (pthread_mutex_lock (&(thread->threadLock)) == 0)
|
||||
{
|
||||
/*
|
||||
* Since we are starting at the head of the key's threads
|
||||
* chain, this will also point key->threads at the next assoc.
|
||||
* While we hold key->keyLock, no other thread can insert
|
||||
* a new assoc via pthread_setspecific.
|
||||
*/
|
||||
pte_tkAssocDestroy (assoc);
|
||||
(void) pthread_mutex_unlock (&(thread->threadLock));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Thread or lock is no longer valid? */
|
||||
pte_tkAssocDestroy (assoc);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock (&(key->keyLock));
|
||||
}
|
||||
|
||||
pte_osTlsFree (key->key);
|
||||
if (key->destructor != NULL)
|
||||
{
|
||||
/* A thread could be holding the keyLock */
|
||||
while (EBUSY == (result = pthread_mutex_destroy (&(key->keyLock))))
|
||||
pte_osThreadSleep(1); /* Ugly. */
|
||||
}
|
||||
|
||||
free (key);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-500
@@ -1,500 +0,0 @@
|
||||
/*
|
||||
* pthread_mutex.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements mutual exclusion (mutex) primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pte_osal.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
#define TEST_IE InterlockedExchange
|
||||
|
||||
int pthread_mutex_destroy (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check to see if we have something to delete.
|
||||
*/
|
||||
if (*mutex < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
mx = *mutex;
|
||||
|
||||
result = pthread_mutex_trylock (&mx);
|
||||
|
||||
/*
|
||||
* If trylock succeeded and the mutex is not recursively locked it
|
||||
* can be destroyed.
|
||||
*/
|
||||
if (result == 0)
|
||||
{
|
||||
if (mx->kind != PTHREAD_MUTEX_RECURSIVE || 1 == mx->recursive_count)
|
||||
{
|
||||
/*
|
||||
* FIXME!!!
|
||||
* The mutex isn't held by another thread but we could still
|
||||
* be too late invalidating the mutex below since another thread
|
||||
* may already have entered mutex_lock and the check for a valid
|
||||
* *mutex != NULL.
|
||||
*
|
||||
* Note that this would be an unusual situation because it is not
|
||||
* common that mutexes are destroyed while they are still in
|
||||
* use by other threads.
|
||||
*/
|
||||
*mutex = NULL;
|
||||
|
||||
result = pthread_mutex_unlock (&mx);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
pte_osSemaphoreDelete(mx->handle);
|
||||
|
||||
free(mx);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Restore the mutex before we return the error.
|
||||
*/
|
||||
*mutex = mx;
|
||||
}
|
||||
}
|
||||
else /* mx->recursive_count > 1 */
|
||||
{
|
||||
/*
|
||||
* The mutex must be recursive and already locked by us (this thread).
|
||||
*/
|
||||
mx->recursive_count--; /* Undo effect of pthread_mutex_trylock() above */
|
||||
result = EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_mutex_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_mutex_test_init_lock);
|
||||
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised mutex that has not yet been used (initialised).
|
||||
* If we get to here, another thread
|
||||
* waiting to initialise this mutex will get an EINVAL.
|
||||
*/
|
||||
*mutex = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The mutex has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_mutex_test_init_lock);
|
||||
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
if (mutex == NULL)
|
||||
return EINVAL;
|
||||
|
||||
mx = (pthread_mutex_t) calloc (1, sizeof (*mx));
|
||||
|
||||
if (mx == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
{
|
||||
mx->lock_idx = 0;
|
||||
mx->recursive_count = 0;
|
||||
mx->kind = (attr == NULL || *attr == NULL
|
||||
? PTHREAD_MUTEX_DEFAULT : (*attr)->kind);
|
||||
mx->ownerThread = NULL;
|
||||
|
||||
pte_osSemaphoreCreate(0,&mx->handle);
|
||||
|
||||
}
|
||||
|
||||
*mutex = mx;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutex_lock (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
if (*mutex == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static mutex. We check
|
||||
* again inside the guarded section of pte_mutex_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if ((result = pte_mutex_check_need_init (mutex)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
mx = *mutex;
|
||||
|
||||
if (mx->kind == PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
if (PTE_ATOMIC_EXCHANGE(
|
||||
&mx->lock_idx,
|
||||
1) != 0)
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (pte_osSemaphorePend(mx->handle,NULL) != PTE_OS_OK)
|
||||
{
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
if (PTE_ATOMIC_COMPARE_EXCHANGE(&mx->lock_idx,1,0) == 0)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_equal (mx->ownerThread, self))
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_RECURSIVE)
|
||||
mx->recursive_count++;
|
||||
else
|
||||
result = EDEADLK;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (pte_osSemaphorePend(mx->handle,NULL) != PTE_OS_OK)
|
||||
{
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static int pte_timed_eventwait (pte_osSemaphoreHandle event, const struct timespec *abstime)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DESCRIPTION
|
||||
* This function waits on an event until signaled or until
|
||||
* abstime passes.
|
||||
* If abstime has passed when this routine is called then
|
||||
* it returns a result to indicate this.
|
||||
*
|
||||
* If 'abstime' is a NULL pointer then this function will
|
||||
* block until it can successfully decrease the value or
|
||||
* until interrupted by a signal.
|
||||
*
|
||||
* This routine is not a cancelation point.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully signaled,
|
||||
* ETIMEDOUT abstime passed
|
||||
* EINVAL 'event' is not a valid event,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
unsigned int milliseconds;
|
||||
pte_osResult status;
|
||||
int retval;
|
||||
|
||||
if (abstime == NULL)
|
||||
status = pte_osSemaphorePend(event, NULL);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Calculate timeout as milliseconds from current system time.
|
||||
*/
|
||||
milliseconds = pte_relmillisecs (abstime);
|
||||
|
||||
status = pte_osSemaphorePend(event, &milliseconds);
|
||||
}
|
||||
|
||||
|
||||
if (status == PTE_OS_TIMEOUT)
|
||||
{
|
||||
retval = ETIMEDOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
int pthread_mutex_timedlock (pthread_mutex_t * mutex,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static mutex. We check
|
||||
* again inside the guarded section of pte_mutex_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if ((result = pte_mutex_check_need_init (mutex)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
mx = *mutex;
|
||||
|
||||
if (mx->kind == PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
if (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,1) != 0)
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (0 != (result = pte_timed_eventwait (mx->handle, abstime)))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
if (PTE_ATOMIC_COMPARE_EXCHANGE(&mx->lock_idx,1,0) == 0)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_equal (mx->ownerThread, self))
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_RECURSIVE)
|
||||
mx->recursive_count++;
|
||||
else
|
||||
return EDEADLK;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (PTE_ATOMIC_EXCHANGE(&mx->lock_idx,-1) != 0)
|
||||
{
|
||||
if (0 != (result = pte_timed_eventwait (mx->handle, abstime)))
|
||||
return result;
|
||||
}
|
||||
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_mutex_trylock (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static mutex. We check
|
||||
* again inside the guarded section of pte_mutex_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if ((result = pte_mutex_check_need_init (mutex)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
mx = *mutex;
|
||||
|
||||
if (0 == PTE_ATOMIC_COMPARE_EXCHANGE (&mx->lock_idx,1,0))
|
||||
{
|
||||
if (mx->kind != PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
mx->recursive_count = 1;
|
||||
mx->ownerThread = pthread_self ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_RECURSIVE &&
|
||||
pthread_equal (mx->ownerThread, pthread_self ()))
|
||||
mx->recursive_count++;
|
||||
else
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutex_unlock (pthread_mutex_t * mutex)
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutex_t mx = *mutex;
|
||||
|
||||
/*
|
||||
* Let the system deal with invalid pointers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If the thread calling us holds the mutex then there is no
|
||||
* race condition. If another thread holds the
|
||||
* lock then we shouldn't be in here.
|
||||
*/
|
||||
if (mx < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
|
||||
{
|
||||
if (mx->kind == PTHREAD_MUTEX_NORMAL)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = PTE_ATOMIC_EXCHANGE (&mx->lock_idx,0);
|
||||
if (idx != 0)
|
||||
{
|
||||
if (idx < 0)
|
||||
{
|
||||
/*
|
||||
* Someone may be waiting on that mutex.
|
||||
*/
|
||||
if (pte_osSemaphorePost(mx->handle,1) != PTE_OS_OK)
|
||||
result = EINVAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Was not locked (so can't be owned by us).
|
||||
*/
|
||||
result = EPERM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_equal (mx->ownerThread, pthread_self ()))
|
||||
{
|
||||
if (mx->kind != PTHREAD_MUTEX_RECURSIVE
|
||||
|| 0 == --mx->recursive_count)
|
||||
{
|
||||
mx->ownerThread = NULL;
|
||||
|
||||
if (PTE_ATOMIC_EXCHANGE (&mx->lock_idx,0) < 0)
|
||||
{
|
||||
if (pte_osSemaphorePost(mx->handle,1) != PTE_OS_OK)
|
||||
result = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
result = EPERM;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-377
@@ -1,377 +0,0 @@
|
||||
/*
|
||||
* pthread_mutexattr.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements mutual exclusion (mutex) primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int pthread_mutexattr_destroy (pthread_mutexattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a mutex attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a mutex attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect mutexes created using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
pthread_mutexattr_t ma = *attr;
|
||||
|
||||
*attr = NULL;
|
||||
free (ma);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_getkind_np (pthread_mutexattr_t * attr, int *kind)
|
||||
{
|
||||
return pthread_mutexattr_gettype (attr, kind);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr != NULL && *attr != NULL && kind != NULL)
|
||||
*kind = (*attr)->kind;
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
int pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether mutexes created with 'attr' can be
|
||||
* shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared mutexes MUST be allocated in shared
|
||||
* memory.
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_init (pthread_mutexattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a mutex attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a mutex attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Used to define mutex types
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_mutexattr_t ma = (pthread_mutexattr_t) calloc (1, sizeof (*ma));
|
||||
|
||||
if (ma == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
{
|
||||
ma->pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
ma->kind = PTHREAD_MUTEX_DEFAULT;
|
||||
}
|
||||
|
||||
*attr = ma;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_setkind_np (pthread_mutexattr_t * attr, int kind)
|
||||
{
|
||||
return pthread_mutexattr_settype (attr, kind);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Mutexes created with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Mutexes creatd with 'attr' can be shared between
|
||||
* processes if pthread_mutex_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared mutexes MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared mutexes
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((pshared == PTHREAD_PROCESS_SHARED) ||
|
||||
(pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
#else
|
||||
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
*
|
||||
* DOCPUBLIC
|
||||
* The pthread_mutexattr_settype() and
|
||||
* pthread_mutexattr_gettype() functions respectively set and
|
||||
* get the mutex type attribute. This attribute is set in the
|
||||
* type parameter to these functions.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_mutexattr_t
|
||||
*
|
||||
* type
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_MUTEX_DEFAULT
|
||||
*
|
||||
* PTHREAD_MUTEX_NORMAL
|
||||
*
|
||||
* PTHREAD_MUTEX_ERRORCHECK
|
||||
*
|
||||
* PTHREAD_MUTEX_RECURSIVE
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The pthread_mutexattr_settype() and
|
||||
* pthread_mutexattr_gettype() functions respectively set and
|
||||
* get the mutex type attribute. This attribute is set in the
|
||||
* type parameter to these functions. The default value of the
|
||||
* type attribute is PTHREAD_MUTEX_DEFAULT.
|
||||
*
|
||||
* The type of mutex is contained in the type attribute of the
|
||||
* mutex attributes. Valid mutex types include:
|
||||
*
|
||||
* PTHREAD_MUTEX_NORMAL
|
||||
* This type of mutex does not detect deadlock. A
|
||||
* thread attempting to relock this mutex without
|
||||
* first unlocking it will deadlock. Attempting to
|
||||
* unlock a mutex locked by a different thread
|
||||
* results in undefined behavior. Attempting to
|
||||
* unlock an unlocked mutex results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* PTHREAD_MUTEX_ERRORCHECK
|
||||
* This type of mutex provides error checking. A
|
||||
* thread attempting to relock this mutex without
|
||||
* first unlocking it will return with an error. A
|
||||
* thread attempting to unlock a mutex which another
|
||||
* thread has locked will return with an error. A
|
||||
* thread attempting to unlock an unlocked mutex will
|
||||
* return with an error.
|
||||
*
|
||||
* PTHREAD_MUTEX_DEFAULT
|
||||
* Same as PTHREAD_MUTEX_NORMAL.
|
||||
*
|
||||
* PTHREAD_MUTEX_RECURSIVE
|
||||
* A thread attempting to relock this mutex without
|
||||
* first unlocking it will succeed in locking the
|
||||
* mutex. The relocking deadlock which can occur with
|
||||
* mutexes of type PTHREAD_MUTEX_NORMAL cannot occur
|
||||
* with this type of mutex. Multiple locks of this
|
||||
* mutex require the same number of unlocks to
|
||||
* release the mutex before another thread can
|
||||
* acquire the mutex. A thread attempting to unlock a
|
||||
* mutex which another thread has locked will return
|
||||
* with an error. A thread attempting to unlock an
|
||||
* unlocked mutex will return with an error. This
|
||||
* type of mutex is only supported for mutexes whose
|
||||
* process shared attribute is
|
||||
* PTHREAD_PROCESS_PRIVATE.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or 'type' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if ((attr != NULL && *attr != NULL))
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case PTHREAD_MUTEX_FAST_NP:
|
||||
case PTHREAD_MUTEX_RECURSIVE_NP:
|
||||
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||
(*attr)->kind = kind;
|
||||
break;
|
||||
default:
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-676
@@ -1,676 +0,0 @@
|
||||
/*
|
||||
* pthread_rwlock.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements read/write lock primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int pthread_rwlock_destroy (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
pthread_rwlock_t rwl;
|
||||
int result = 0, result1 = 0, result2 = 0;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (*rwlock != PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether any threads own/wait for the lock (wait for ex.access);
|
||||
* report "BUSY" if so.
|
||||
*/
|
||||
if (rwl->nExclusiveAccessCount > 0
|
||||
|| rwl->nSharedAccessCount > rwl->nCompletedSharedAccessCount)
|
||||
{
|
||||
result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
result2 = EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
rwl->nMagic = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
pthread_mutex_unlock (&rwl->mtxExclusiveAccess);
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
*rwlock = NULL; /* Invalidate rwlock before anything else */
|
||||
result = pthread_cond_destroy (&(rwl->cndSharedAccessCompleted));
|
||||
result1 = pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted));
|
||||
result2 = pthread_mutex_destroy (&(rwl->mtxExclusiveAccess));
|
||||
(void) free (rwl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_rwlock_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_rwlock_test_init_lock);
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised rwlock that has not yet been used (initialised).
|
||||
* If we get to here, another thread
|
||||
* waiting to initialise this rwlock will get an EINVAL.
|
||||
*/
|
||||
*rwlock = NULL;
|
||||
}
|
||||
/*
|
||||
* The rwlock has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
else
|
||||
result = EBUSY;
|
||||
|
||||
pte_osMutexUnlock(pte_rwlock_test_init_lock);
|
||||
|
||||
}
|
||||
|
||||
return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));
|
||||
}
|
||||
|
||||
int pthread_rwlock_init (pthread_rwlock_t * rwlock,
|
||||
const pthread_rwlockattr_t * attr)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl = 0;
|
||||
|
||||
if (rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (attr != NULL && *attr != NULL)
|
||||
{
|
||||
result = EINVAL; /* Not supported */
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
rwl = (pthread_rwlock_t) calloc (1, sizeof (*rwl));
|
||||
|
||||
if (rwl == NULL)
|
||||
{
|
||||
result = ENOMEM;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount = 0;
|
||||
rwl->nExclusiveAccessCount = 0;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
result = pthread_mutex_init (&rwl->mtxExclusiveAccess, NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
goto FAIL0;
|
||||
}
|
||||
|
||||
result = pthread_mutex_init (&rwl->mtxSharedAccessCompleted, NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
goto FAIL1;
|
||||
}
|
||||
|
||||
result = pthread_cond_init (&rwl->cndSharedAccessCompleted, NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
goto FAIL2;
|
||||
}
|
||||
|
||||
rwl->nMagic = PTE_RWLOCK_MAGIC;
|
||||
|
||||
result = 0;
|
||||
goto DONE;
|
||||
|
||||
FAIL2:
|
||||
(void) pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted));
|
||||
|
||||
FAIL1:
|
||||
(void) pthread_mutex_destroy (&(rwl->mtxExclusiveAccess));
|
||||
|
||||
FAIL0:
|
||||
(void) free (rwl);
|
||||
rwl = NULL;
|
||||
|
||||
DONE:
|
||||
*rwlock = rwl;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_rwlock_rdlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
if (++rwl->nSharedAccessCount == INT_MAX)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)));
|
||||
}
|
||||
|
||||
int pthread_rwlock_timedrdlock (pthread_rwlock_t * rwlock,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (++rwl->nSharedAccessCount == INT_MAX)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted),
|
||||
abstime)) != 0)
|
||||
{
|
||||
if (result == ETIMEDOUT)
|
||||
{
|
||||
++rwl->nCompletedSharedAccessCount;
|
||||
}
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)));
|
||||
}
|
||||
|
||||
int pthread_rwlock_timedwrlock (pthread_rwlock_t * rwlock,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted),
|
||||
abstime)) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if (rwl->nCompletedSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
}
|
||||
|
||||
if (rwl->nSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;
|
||||
|
||||
/*
|
||||
* This routine may be a cancelation point
|
||||
* according to POSIX 1003.1j section 18.1.2.
|
||||
*/
|
||||
pthread_cleanup_push (pte_rwlock_cancelwrwait, (void *) rwl);
|
||||
|
||||
do
|
||||
{
|
||||
result =
|
||||
pthread_cond_timedwait (&(rwl->cndSharedAccessCompleted),
|
||||
&(rwl->mtxSharedAccessCompleted),
|
||||
abstime);
|
||||
}
|
||||
while (result == 0 && rwl->nCompletedSharedAccessCount < 0);
|
||||
|
||||
pthread_cleanup_pop ((result != 0) ? 1 : 0);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
rwl->nSharedAccessCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
rwl->nExclusiveAccessCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_rwlock_tryrdlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (++rwl->nSharedAccessCount == INT_MAX)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return (pthread_mutex_unlock (&rwl->mtxExclusiveAccess));
|
||||
}
|
||||
|
||||
int pthread_rwlock_trywrlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result, result1;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_trylock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return ((result1 != 0) ? result1 : result);
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if (rwl->nCompletedSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
}
|
||||
|
||||
if (rwl->nSharedAccessCount > 0)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result =
|
||||
pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) == 0)
|
||||
{
|
||||
result = EBUSY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rwl->nExclusiveAccessCount = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_rwlock_unlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result, result1;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* Assume any race condition here is harmless.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if ((result =
|
||||
pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (++rwl->nCompletedSharedAccessCount == 0)
|
||||
{
|
||||
result = pthread_cond_signal (&(rwl->cndSharedAccessCompleted));
|
||||
}
|
||||
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));
|
||||
}
|
||||
else
|
||||
{
|
||||
rwl->nExclusiveAccessCount--;
|
||||
|
||||
result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));
|
||||
result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
|
||||
}
|
||||
|
||||
return ((result != 0) ? result : result1);
|
||||
}
|
||||
|
||||
int pthread_rwlock_wrlock (pthread_rwlock_t * rwlock)
|
||||
{
|
||||
int result;
|
||||
pthread_rwlock_t rwl;
|
||||
|
||||
if (rwlock == NULL || *rwlock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* We do a quick check to see if we need to do more work
|
||||
* to initialise a static rwlock. We check
|
||||
* again inside the guarded section of pte_rwlock_check_need_init()
|
||||
* to avoid race conditions.
|
||||
*/
|
||||
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
||||
{
|
||||
result = pte_rwlock_check_need_init (rwlock);
|
||||
|
||||
if (result != 0 && result != EBUSY)
|
||||
return result;
|
||||
}
|
||||
|
||||
rwl = *rwlock;
|
||||
|
||||
if (rwl->nMagic != PTE_RWLOCK_MAGIC)
|
||||
return EINVAL;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)
|
||||
return result;
|
||||
|
||||
if ((result = pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (rwl->nExclusiveAccessCount == 0)
|
||||
{
|
||||
if (rwl->nCompletedSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;
|
||||
rwl->nCompletedSharedAccessCount = 0;
|
||||
}
|
||||
|
||||
if (rwl->nSharedAccessCount > 0)
|
||||
{
|
||||
rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;
|
||||
|
||||
/*
|
||||
* This routine may be a cancelation point
|
||||
* according to POSIX 1003.1j section 18.1.2.
|
||||
*/
|
||||
pthread_cleanup_push (pte_rwlock_cancelwrwait, (void *) rwl);
|
||||
|
||||
do
|
||||
{
|
||||
result = pthread_cond_wait (&(rwl->cndSharedAccessCompleted),
|
||||
&(rwl->mtxSharedAccessCompleted));
|
||||
}
|
||||
while (result == 0 && rwl->nCompletedSharedAccessCount < 0);
|
||||
|
||||
pthread_cleanup_pop ((result != 0) ? 1 : 0);
|
||||
|
||||
if (result == 0)
|
||||
rwl->nSharedAccessCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
rwl->nExclusiveAccessCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
Vendored
-259
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* pthread_rwlockattr_destroy.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements read/write lock primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
int
|
||||
pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Destroys a rwlock attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Destroys a rwlock attributes object. The object can
|
||||
* no longer be used.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Does not affect rwlockss created using 'attr'
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully released attr,
|
||||
* EINVAL 'attr' is invalid.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (attr == NULL || *attr == NULL)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_rwlockattr_t rwa = *attr;
|
||||
|
||||
*attr = NULL;
|
||||
free (rwa);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,
|
||||
int *pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Determine whether rwlocks created with 'attr' can be
|
||||
* shared between processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
* pshared
|
||||
* will be set to one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Rwlocks creatd with 'attr' can be shared between
|
||||
* processes if pthread_rwlock_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
* NOTES:
|
||||
* 1) pshared rwlocks MUST be allocated in shared
|
||||
* memory.
|
||||
* 2) The following macro is defined if shared rwlocks
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully retrieved attribute,
|
||||
* EINVAL 'attr' is invalid,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) && (pshared != NULL))
|
||||
{
|
||||
*pshared = (*attr)->pshared;
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_init (pthread_rwlockattr_t * attr)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Initializes a rwlock attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes a rwlock attributes object with default
|
||||
* attributes.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully initialized attr,
|
||||
* ENOMEM insufficient memory for attr.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_rwlockattr_t rwa;
|
||||
|
||||
rwa = (pthread_rwlockattr_t) calloc (1, sizeof (*rwa));
|
||||
|
||||
if (rwa == NULL)
|
||||
result = ENOMEM;
|
||||
else
|
||||
rwa->pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
*attr = rwa;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, int pshared)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* Rwlocks created with 'attr' can be shared between
|
||||
* processes if pthread_rwlock_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* PARAMETERS
|
||||
* attr
|
||||
* pointer to an instance of pthread_rwlockattr_t
|
||||
*
|
||||
* pshared
|
||||
* must be one of:
|
||||
*
|
||||
* PTHREAD_PROCESS_SHARED
|
||||
* May be shared if in shared memory
|
||||
*
|
||||
* PTHREAD_PROCESS_PRIVATE
|
||||
* Cannot be shared.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Rwlocks creatd with 'attr' can be shared between
|
||||
* processes if pthread_rwlock_t variable is allocated
|
||||
* in memory shared by these processes.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) pshared rwlocks MUST be allocated in shared
|
||||
* memory.
|
||||
*
|
||||
* 2) The following macro is defined if shared rwlocks
|
||||
* are supported:
|
||||
* _POSIX_THREAD_PROCESS_SHARED
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set attribute,
|
||||
* EINVAL 'attr' or pshared is invalid,
|
||||
* ENOSYS PTHREAD_PROCESS_SHARED not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((attr != NULL && *attr != NULL) &&
|
||||
((pshared == PTHREAD_PROCESS_SHARED) ||
|
||||
(pshared == PTHREAD_PROCESS_PRIVATE)))
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
|
||||
#if !defined( _POSIX_THREAD_PROCESS_SHARED )
|
||||
|
||||
result = ENOSYS;
|
||||
pshared = PTHREAD_PROCESS_PRIVATE;
|
||||
|
||||
#else
|
||||
|
||||
result = 0;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
(*attr)->pshared = pshared;
|
||||
}
|
||||
else
|
||||
result = EINVAL;
|
||||
|
||||
return (result);
|
||||
|
||||
} /* pthread_rwlockattr_setpshared */
|
||||
Vendored
-399
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* pthread_set.c
|
||||
*
|
||||
* Description:
|
||||
* POSIX thread functions related to state.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int pthread_setcancelstate (int state, int *oldstate)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability state to 'state' and returns the previous
|
||||
* cancelability state at the location referenced by
|
||||
* 'oldstate'
|
||||
*
|
||||
* PARAMETERS
|
||||
* state,
|
||||
* oldstate
|
||||
* PTHREAD_CANCEL_ENABLE
|
||||
* cancellation is enabled,
|
||||
*
|
||||
* PTHREAD_CANCEL_DISABLE
|
||||
* cancellation is disabled
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability state to 'state' and returns the previous
|
||||
* cancelability state at the location referenced by
|
||||
* 'oldstate'.
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Use to disable cancellation around 'atomic' code that
|
||||
* includes cancellation points
|
||||
*
|
||||
* COMPATIBILITY ADDITIONS
|
||||
* If 'oldstate' is NULL then the previous state is not returned
|
||||
* but the function still succeeds. (Solaris)
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set cancelability type,
|
||||
* EINVAL 'state' is invalid
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_t self = pthread_self ();
|
||||
pte_thread_t * sp = (pte_thread_t *) self;
|
||||
|
||||
if (sp == NULL
|
||||
|| (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE))
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Lock for async-cancel safety.
|
||||
*/
|
||||
(void) pthread_mutex_lock (&sp->cancelLock);
|
||||
|
||||
if (oldstate != NULL)
|
||||
*oldstate = sp->cancelState;
|
||||
|
||||
sp->cancelState = state;
|
||||
|
||||
/*
|
||||
* Check if there is a pending asynchronous cancel
|
||||
*/
|
||||
if (state == PTHREAD_CANCEL_ENABLE
|
||||
&& (sp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||
&& (pte_osThreadCheckCancel(sp->threadId) == PTE_OS_INTERRUPTED) )
|
||||
{
|
||||
sp->state = PThreadStateCanceling;
|
||||
sp->cancelState = PTHREAD_CANCEL_DISABLE;
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
pte_throw (PTE_EPS_CANCEL);
|
||||
|
||||
/* Never reached */
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_setcanceltype (int type, int *oldtype)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability type to 'type' and returns the previous
|
||||
* cancelability type at the location referenced by
|
||||
* 'oldtype'
|
||||
*
|
||||
* PARAMETERS
|
||||
* type,
|
||||
* oldtype
|
||||
* PTHREAD_CANCEL_DEFERRED
|
||||
* only deferred cancelation is allowed,
|
||||
*
|
||||
* PTHREAD_CANCEL_ASYNCHRONOUS
|
||||
* Asynchronous cancellation is allowed
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function atomically sets the calling thread's
|
||||
* cancelability type to 'type' and returns the previous
|
||||
* cancelability type at the location referenced by
|
||||
* 'oldtype'
|
||||
*
|
||||
* NOTES:
|
||||
* 1) Use with caution; most code is not safe for use
|
||||
* with asynchronous cancelability.
|
||||
*
|
||||
* COMPATIBILITY ADDITIONS
|
||||
* If 'oldtype' is NULL then the previous type is not returned
|
||||
* but the function still succeeds. (Solaris)
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set cancelability type,
|
||||
* EINVAL 'type' is invalid
|
||||
* EPERM Async cancellation is not supported.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
pthread_t self = pthread_self ();
|
||||
pte_thread_t * sp = (pte_thread_t *) self;
|
||||
|
||||
#ifndef PTE_SUPPORT_ASYNC_CANCEL
|
||||
if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||
{
|
||||
/* Async cancellation is not supported at this time. See notes in
|
||||
* pthread_cancel.
|
||||
*/
|
||||
return EPERM;
|
||||
}
|
||||
#endif /* PTE_SUPPORT_ASYNC_CANCEL */
|
||||
|
||||
if (sp == NULL
|
||||
|| (type != PTHREAD_CANCEL_DEFERRED
|
||||
&& type != PTHREAD_CANCEL_ASYNCHRONOUS))
|
||||
return EINVAL;
|
||||
|
||||
/*
|
||||
* Lock for async-cancel safety.
|
||||
*/
|
||||
(void) pthread_mutex_lock (&sp->cancelLock);
|
||||
|
||||
if (oldtype != NULL)
|
||||
*oldtype = sp->cancelType;
|
||||
|
||||
sp->cancelType = type;
|
||||
|
||||
/*
|
||||
* Check if there is a pending asynchronous cancel
|
||||
*/
|
||||
|
||||
if (sp->cancelState == PTHREAD_CANCEL_ENABLE
|
||||
&& (type == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||
&& (pte_osThreadCheckCancel(sp->threadId) == PTE_OS_INTERRUPTED) )
|
||||
{
|
||||
sp->state = PThreadStateCanceling;
|
||||
sp->cancelState = PTHREAD_CANCEL_DISABLE;
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
pte_throw (PTE_EPS_CANCEL);
|
||||
|
||||
/* Never reached */
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&sp->cancelLock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_setconcurrency (int level)
|
||||
{
|
||||
if (level < 0)
|
||||
return EINVAL;
|
||||
|
||||
pte_concurrency = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_setschedparam (pthread_t thread, int policy,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Validate the thread id. */
|
||||
result = pthread_kill (thread, 0);
|
||||
if (0 != result)
|
||||
return result;
|
||||
|
||||
/* Validate the scheduling policy. */
|
||||
if (policy < SCHED_MIN || policy > SCHED_MAX)
|
||||
return EINVAL;
|
||||
|
||||
/* Ensure the policy is SCHED_OTHER. */
|
||||
if (policy != SCHED_OTHER)
|
||||
return ENOTSUP;
|
||||
|
||||
return (pte_setthreadpriority (thread, policy, param->sched_priority));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pte_setthreadpriority (pthread_t thread, int policy, int priority)
|
||||
{
|
||||
int prio;
|
||||
int result;
|
||||
pte_thread_t * tp = (pte_thread_t *) thread;
|
||||
|
||||
prio = priority;
|
||||
|
||||
/* Validate priority level. */
|
||||
if (prio < sched_get_priority_min (policy) ||
|
||||
prio > sched_get_priority_max (policy))
|
||||
return EINVAL;
|
||||
|
||||
result = pthread_mutex_lock (&tp->threadLock);
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
/* If this fails, the current priority is unchanged. */
|
||||
|
||||
if (0 != pte_osThreadSetPriority(tp->threadId, prio))
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Must record the thread's sched_priority as given,
|
||||
* not as finally adjusted.
|
||||
*/
|
||||
tp->sched_priority = priority;
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&tp->threadLock);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int pthread_setspecific (pthread_key_t key, const void *value)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function sets the value of the thread specific
|
||||
* key in the calling thread.
|
||||
*
|
||||
* PARAMETERS
|
||||
* key
|
||||
* an instance of pthread_key_t
|
||||
* value
|
||||
* the value to set key to
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function sets the value of the thread specific
|
||||
* key in the calling thread.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully set value
|
||||
* EAGAIN could not set value
|
||||
* ENOENT SERIOUS!!
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pthread_t self;
|
||||
int result = 0;
|
||||
|
||||
if (key != pte_selfThreadKey)
|
||||
{
|
||||
/*
|
||||
* Using pthread_self will implicitly create
|
||||
* an instance of pthread_t for the current
|
||||
* thread if one wasn't explicitly created
|
||||
*/
|
||||
self = pthread_self ();
|
||||
if (self == NULL)
|
||||
return ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Resolve catch-22 of registering thread with selfThread
|
||||
* key
|
||||
*/
|
||||
pte_thread_t * sp = (pte_thread_t *) pthread_getspecific (pte_selfThreadKey);
|
||||
|
||||
if (sp == NULL)
|
||||
{
|
||||
if (value == NULL)
|
||||
return ENOENT;
|
||||
|
||||
self = *((pthread_t *) value);
|
||||
}
|
||||
else
|
||||
self = sp;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
if (key != NULL)
|
||||
{
|
||||
if (self != NULL && key->destructor != NULL && value != NULL)
|
||||
{
|
||||
/*
|
||||
* Only require associations if we have to
|
||||
* call user destroy routine.
|
||||
* Don't need to locate an existing association
|
||||
* when setting data to NULL since the
|
||||
* data is stored with the operating system; not
|
||||
* on the association; setting assoc to NULL short
|
||||
* circuits the search.
|
||||
*/
|
||||
ThreadKeyAssoc *assoc;
|
||||
|
||||
if (pthread_mutex_lock(&(key->keyLock)) == 0)
|
||||
{
|
||||
pte_thread_t * sp = (pte_thread_t *) self;
|
||||
|
||||
(void) pthread_mutex_lock(&(sp->threadLock));
|
||||
|
||||
assoc = (ThreadKeyAssoc *) sp->keys;
|
||||
/*
|
||||
* Locate existing association
|
||||
*/
|
||||
while (assoc != NULL)
|
||||
{
|
||||
/*
|
||||
* Association already exists
|
||||
*/
|
||||
if (assoc->key == key)
|
||||
break;
|
||||
assoc = assoc->nextKey;
|
||||
}
|
||||
|
||||
/*
|
||||
* create an association if not found
|
||||
*/
|
||||
if (assoc == NULL)
|
||||
result = pte_tkAssocCreate (sp, key);
|
||||
|
||||
(void) pthread_mutex_unlock(&(sp->threadLock));
|
||||
}
|
||||
(void) pthread_mutex_unlock(&(key->keyLock));
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
if (pte_osTlsSetValue (key->key, (void *) value) != PTE_OS_OK)
|
||||
result = EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
Vendored
-295
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* pthread_spin.c
|
||||
*
|
||||
* Description:
|
||||
* This translation unit implements spin lock primitives.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
|
||||
|
||||
int
|
||||
pthread_spin_destroy (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
int result = 0;
|
||||
|
||||
if (lock == NULL || *lock == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((s = *lock) != PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
if (s->interlock == PTE_SPIN_USE_MUTEX)
|
||||
{
|
||||
result = pthread_mutex_destroy (&(s->u.mutex));
|
||||
}
|
||||
else if (PTE_SPIN_UNLOCKED !=
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (
|
||||
& (s->interlock),
|
||||
(int) PTE_OBJECT_INVALID,
|
||||
PTE_SPIN_UNLOCKED))
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
/*
|
||||
* We are relying on the application to ensure that all other threads
|
||||
* have finished with the spinlock before destroying it.
|
||||
*/
|
||||
*lock = NULL;
|
||||
(void) free (s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* See notes in pte_spinlock_check_need_init() above also.
|
||||
*/
|
||||
|
||||
pte_osMutexLock (pte_spinlock_test_init_lock);
|
||||
|
||||
/*
|
||||
* Check again.
|
||||
*/
|
||||
if (*lock == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
/*
|
||||
* This is all we need to do to destroy a statically
|
||||
* initialised spinlock that has not yet been used (initialised).
|
||||
* If we get to here, another thread
|
||||
* waiting to initialise this mutex will get an EINVAL.
|
||||
*/
|
||||
*lock = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The spinlock has been initialised while we were waiting
|
||||
* so assume it's in use.
|
||||
*/
|
||||
result = EBUSY;
|
||||
}
|
||||
|
||||
pte_osMutexUnlock(pte_spinlock_test_init_lock);
|
||||
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_spin_init (pthread_spinlock_t * lock, int pshared)
|
||||
{
|
||||
pthread_spinlock_t s;
|
||||
int cpus = 0;
|
||||
int result = 0;
|
||||
|
||||
if (lock == NULL)
|
||||
return EINVAL;
|
||||
|
||||
if (0 != pte_getprocessors (&cpus))
|
||||
{
|
||||
cpus = 1;
|
||||
}
|
||||
|
||||
if (cpus > 1)
|
||||
{
|
||||
if (pshared == PTHREAD_PROCESS_SHARED)
|
||||
{
|
||||
/*
|
||||
* Creating spinlock that can be shared between
|
||||
* processes.
|
||||
*/
|
||||
#if _POSIX_THREAD_PROCESS_SHARED >= 0
|
||||
|
||||
/*
|
||||
* Not implemented yet.
|
||||
*/
|
||||
|
||||
#error ERROR [__FILE__, line __LINE__]: Process shared spin locks are not supported yet.
|
||||
|
||||
#else
|
||||
|
||||
return ENOSYS;
|
||||
|
||||
#endif /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
s = (pthread_spinlock_t) calloc (1, sizeof (*s));
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (cpus > 1)
|
||||
{
|
||||
s->u.cpus = cpus;
|
||||
s->interlock = PTE_SPIN_UNLOCKED;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutexattr_t ma;
|
||||
result = pthread_mutexattr_init (&ma);
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
ma->pshared = pshared;
|
||||
result = pthread_mutex_init (&(s->u.mutex), &ma);
|
||||
if (0 == result)
|
||||
{
|
||||
s->interlock = PTE_SPIN_USE_MUTEX;
|
||||
}
|
||||
}
|
||||
(void) pthread_mutexattr_destroy (&ma);
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
{
|
||||
*lock = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) free (s);
|
||||
*lock = NULL;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int pthread_spin_lock (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
|
||||
if (NULL == lock || NULL == *lock)
|
||||
return (EINVAL);
|
||||
|
||||
if (*lock == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = pte_spinlock_check_need_init (lock)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
s = *lock;
|
||||
|
||||
while ( PTE_SPIN_LOCKED ==
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (&(s->interlock),
|
||||
PTE_SPIN_LOCKED,
|
||||
PTE_SPIN_UNLOCKED))
|
||||
{
|
||||
}
|
||||
|
||||
if (s->interlock == PTE_SPIN_LOCKED)
|
||||
return 0;
|
||||
|
||||
if (s->interlock == PTE_SPIN_USE_MUTEX)
|
||||
return pthread_mutex_lock (&(s->u.mutex));
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_spin_trylock (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
|
||||
if (NULL == lock || NULL == *lock)
|
||||
return (EINVAL);
|
||||
|
||||
if (*lock == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = pte_spinlock_check_need_init (lock)) != 0)
|
||||
return (result);
|
||||
}
|
||||
|
||||
s = *lock;
|
||||
|
||||
switch ((long)
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (&(s->interlock),
|
||||
PTE_SPIN_LOCKED,
|
||||
PTE_SPIN_UNLOCKED))
|
||||
{
|
||||
case PTE_SPIN_UNLOCKED:
|
||||
return 0;
|
||||
case PTE_SPIN_LOCKED:
|
||||
return EBUSY;
|
||||
case PTE_SPIN_USE_MUTEX:
|
||||
return pthread_mutex_trylock (&(s->u.mutex));
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int pthread_spin_unlock (pthread_spinlock_t * lock)
|
||||
{
|
||||
register pthread_spinlock_t s;
|
||||
|
||||
if (NULL == lock || NULL == *lock)
|
||||
return (EINVAL);
|
||||
|
||||
s = *lock;
|
||||
|
||||
if (s == PTHREAD_SPINLOCK_INITIALIZER)
|
||||
return EPERM;
|
||||
|
||||
switch ((long)
|
||||
PTE_ATOMIC_COMPARE_EXCHANGE (&(s->interlock),
|
||||
PTE_SPIN_UNLOCKED,
|
||||
PTE_SPIN_LOCKED))
|
||||
{
|
||||
case PTE_SPIN_LOCKED:
|
||||
return 0;
|
||||
case PTE_SPIN_UNLOCKED:
|
||||
return EPERM;
|
||||
case PTE_SPIN_USE_MUTEX:
|
||||
return pthread_mutex_unlock (&(s->u.mutex));
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
Vendored
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* sched_setscheduler.c
|
||||
*
|
||||
* Description:
|
||||
* POSIX thread functions that deal with thread scheduling.
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "implement.h"
|
||||
#include "sched.h"
|
||||
|
||||
int
|
||||
sched_setscheduler (pid_t pid, int policy)
|
||||
{
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
sched_yield (void)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function indicates that the calling thread is
|
||||
* willing to give up some time slices to other threads.
|
||||
*
|
||||
* PARAMETERS
|
||||
* N/A
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function indicates that the calling thread is
|
||||
* willing to give up some time slices to other threads.
|
||||
* NOTE: Since this is part of POSIX 1003.1b
|
||||
* (realtime extensions), it is defined as returning
|
||||
* -1 if an error occurs and sets errno to the actual
|
||||
* error.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created semaphore,
|
||||
* ENOSYS sched_yield not supported,
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
pte_osThreadSleep (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sched_get_priority_min (int policy)
|
||||
{
|
||||
return pte_osThreadGetMinPriority();
|
||||
}
|
||||
|
||||
int
|
||||
sched_get_priority_max (int policy)
|
||||
{
|
||||
return pte_osThreadGetMaxPriority();
|
||||
}
|
||||
Vendored
-137
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Module: sched.h
|
||||
*
|
||||
* Purpose:
|
||||
* Provides an implementation of POSIX realtime extensions
|
||||
* as defined in
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifndef _SCHED_H
|
||||
#define _SCHED_H
|
||||
|
||||
#include <pte_types.h>
|
||||
|
||||
#undef PTE_LEVEL
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 1
|
||||
/* Include 1b, 1c and 1d */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
#define PTE_LEVEL_MAX 3
|
||||
|
||||
#if !defined(PTE_LEVEL)
|
||||
#define PTE_LEVEL PTE_LEVEL_MAX
|
||||
/* Include everything */
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
/* Thread scheduling policies */
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_OTHER = 0,
|
||||
SCHED_FIFO,
|
||||
SCHED_RR,
|
||||
SCHED_MIN = SCHED_OTHER,
|
||||
SCHED_MAX = SCHED_RR
|
||||
};
|
||||
|
||||
struct sched_param
|
||||
{
|
||||
int sched_priority;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sched_yield (void);
|
||||
|
||||
int sched_get_priority_min (int policy);
|
||||
|
||||
int sched_get_priority_max (int policy);
|
||||
|
||||
int sched_setscheduler (pid_t pid, int policy);
|
||||
|
||||
/*
|
||||
* Note that this macro returns ENOTSUP rather than
|
||||
* ENOSYS as might be expected. However, returning ENOSYS
|
||||
* should mean that sched_get_priority_{min,max} are
|
||||
* not implemented as well as sched_rr_get_interval.
|
||||
* This is not the case, since we just don't support
|
||||
* round-robin scheduling. Therefore I have chosen to
|
||||
* return the same value as sched_setscheduler when
|
||||
* SCHED_RR is passed to it.
|
||||
*/
|
||||
#define sched_rr_get_interval(_pid, _interval) \
|
||||
( errno = ENOTSUP, (int) -1 )
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#undef PTE_LEVEL
|
||||
#undef PTE_LEVEL_MAX
|
||||
|
||||
#endif /* !_SCHED_H */
|
||||
|
||||
Vendored
-895
@@ -1,895 +0,0 @@
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* Module: sem.c
|
||||
*
|
||||
* Purpose:
|
||||
* Semaphores aren't actually part of the PThreads standard.
|
||||
* They are defined by the POSIX Standard:
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pte_osal.h"
|
||||
|
||||
#include "pthread.h"
|
||||
#include "semaphore.h"
|
||||
#include "implement.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sem_t sem;
|
||||
int * resultPtr;
|
||||
} sem_timedwait_cleanup_args_t;
|
||||
|
||||
static void pte_sem_wait_cleanup(void * sem)
|
||||
{
|
||||
sem_t s = (sem_t) sem;
|
||||
unsigned int timeout;
|
||||
|
||||
if (pthread_mutex_lock (&s->lock) == 0)
|
||||
{
|
||||
/*
|
||||
* If sema is destroyed do nothing, otherwise:-
|
||||
* If the sema is posted between us being cancelled and us locking
|
||||
* the sema again above then we need to consume that post but cancel
|
||||
* anyway. If we don't get the semaphore we indicate that we're no
|
||||
* longer waiting.
|
||||
*/
|
||||
timeout = 0;
|
||||
if (pte_osSemaphorePend(s->sem, &timeout) != PTE_OS_OK)
|
||||
{
|
||||
++s->value;
|
||||
|
||||
/*
|
||||
* Don't release the W32 sema, it doesn't need adjustment
|
||||
* because it doesn't record the number of waiters.
|
||||
*/
|
||||
|
||||
}
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void pte_sem_timedwait_cleanup (void * args)
|
||||
{
|
||||
sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args;
|
||||
sem_t s = a->sem;
|
||||
|
||||
if (pthread_mutex_lock (&s->lock) == 0)
|
||||
{
|
||||
/*
|
||||
* We either timed out or were cancelled.
|
||||
* If someone has posted between then and now we try to take the semaphore.
|
||||
* Otherwise the semaphore count may be wrong after we
|
||||
* return. In the case of a cancellation, it is as if we
|
||||
* were cancelled just before we return (after taking the semaphore)
|
||||
* which is ok.
|
||||
*/
|
||||
unsigned int timeout = 0;
|
||||
if (pte_osSemaphorePend(s->sem, &timeout) == PTE_OS_OK)
|
||||
{
|
||||
/* We got the semaphore on the second attempt */
|
||||
*(a->resultPtr) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Indicate we're no longer waiting */
|
||||
s->value++;
|
||||
|
||||
/*
|
||||
* Don't release the OS sema, it doesn't need adjustment
|
||||
* because it doesn't record the number of waiters.
|
||||
*/
|
||||
|
||||
}
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
int sem_close (sem_t * sem)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sem_destroy (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function destroys an unnamed semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function destroys an unnamed semaphore.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully destroyed semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EBUSY threads (or processes) are currently
|
||||
* blocked on 'sem'
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = NULL;
|
||||
|
||||
if (sem == NULL || *sem == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
s = *sem;
|
||||
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
if (s->value < 0)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EBUSY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There are no threads currently blocked on this semaphore. */
|
||||
pte_osResult osResult = pte_osSemaphoreDelete(s->sem);
|
||||
|
||||
if (osResult != PTE_OS_OK)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Invalidate the semaphore handle when we have the lock.
|
||||
* Other sema operations should test this after acquiring the lock
|
||||
* to check that the sema is still valid, i.e. before performing any
|
||||
* operations. This may only be necessary before the sema op routine
|
||||
* returns so that the routine can return EINVAL - e.g. if setting
|
||||
* s->value to SEM_VALUE_MAX below does force a fall-through.
|
||||
*/
|
||||
*sem = NULL;
|
||||
|
||||
/* Prevent anyone else actually waiting on or posting this sema.
|
||||
*/
|
||||
s->value = SEM_VALUE_MAX;
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
do
|
||||
{
|
||||
/* Give other threads a chance to run and exit any sema op
|
||||
* routines. Due to the SEM_VALUE_MAX value, if sem_post or
|
||||
* sem_wait were blocked by us they should fall through.
|
||||
*/
|
||||
pte_osThreadSleep(1);
|
||||
}
|
||||
while (pthread_mutex_destroy (&s->lock) == EBUSY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
free (s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sem_getvalue (sem_t * sem, int *sval)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function stores the current count value of the
|
||||
* semaphore.
|
||||
* RESULTS
|
||||
*
|
||||
* Return value
|
||||
*
|
||||
* 0 sval has been set.
|
||||
* -1 failed, error in errno
|
||||
*
|
||||
* in global errno
|
||||
*
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS this function is not supported,
|
||||
*
|
||||
*
|
||||
* PARAMETERS
|
||||
*
|
||||
* sem pointer to an instance of sem_t
|
||||
*
|
||||
* sval pointer to int.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function stores the current count value of the semaphore
|
||||
* pointed to by sem in the int pointed to by sval.
|
||||
*/
|
||||
{
|
||||
if (sem == NULL || *sem == NULL || sval == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
long value;
|
||||
register sem_t s = *sem;
|
||||
int result = 0;
|
||||
|
||||
if ((result = pthread_mutex_lock(&s->lock)) == 0)
|
||||
{
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = s->value;
|
||||
(void) pthread_mutex_unlock(&s->lock);
|
||||
*sval = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sem_init (sem_t * sem, int pshared, unsigned int value)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function initializes a semaphore. The
|
||||
* initial value of the semaphore is 'value'
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* pshared
|
||||
* if zero, this semaphore may only be shared between
|
||||
* threads in the same process.
|
||||
* if nonzero, the semaphore can be shared between
|
||||
* processes
|
||||
*
|
||||
* value
|
||||
* initial value of the semaphore counter
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function initializes a semaphore. The
|
||||
* initial value of the semaphore is set to 'value'.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully created semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore, or
|
||||
* 'value' >= SEM_VALUE_MAX
|
||||
* ENOMEM out of memory,
|
||||
* ENOSPC a required resource has been exhausted,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EPERM the process lacks appropriate privilege
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = NULL;
|
||||
|
||||
if (pshared != 0)
|
||||
{
|
||||
/*
|
||||
* Creating a semaphore that can be shared between
|
||||
* processes
|
||||
*/
|
||||
result = EPERM;
|
||||
}
|
||||
else if (value > (unsigned int)SEM_VALUE_MAX)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = (sem_t) calloc (1, sizeof (*s));
|
||||
|
||||
if (NULL == s)
|
||||
{
|
||||
result = ENOMEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
s->value = value;
|
||||
if (pthread_mutex_init(&s->lock, NULL) == 0)
|
||||
{
|
||||
|
||||
pte_osResult osResult = pte_osSemaphoreCreate(0, &s->sem);
|
||||
|
||||
|
||||
|
||||
if (osResult != PTE_OS_OK)
|
||||
{
|
||||
(void) pthread_mutex_destroy(&s->lock);
|
||||
result = ENOSPC;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ENOSPC;
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sem = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_open (const char *name, int oflag, mode_t mode, unsigned int value)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
sem_post (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function posts a wakeup to a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function posts a wakeup to a semaphore. If there
|
||||
* are waiting threads (or processes), one is awakened;
|
||||
* otherwise, the semaphore value is incremented by one.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully posted semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* ERANGE semaphore count is too big
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
if (s == NULL)
|
||||
result = EINVAL;
|
||||
else if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->value < SEM_VALUE_MAX)
|
||||
{
|
||||
pte_osResult osResult = pte_osSemaphorePost(s->sem, 1);
|
||||
|
||||
if (++s->value <= 0
|
||||
&& (osResult != PTE_OS_OK))
|
||||
{
|
||||
s->value--;
|
||||
result = EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
result = ERANGE;
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sem_post_multiple (sem_t * sem, int count)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function posts multiple wakeups to a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* count
|
||||
* counter, must be greater than zero.
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function posts multiple wakeups to a semaphore. If there
|
||||
* are waiting threads (or processes), n <= count are awakened;
|
||||
* the semaphore value is incremented by count - n.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully posted semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore
|
||||
* or count is less than or equal to zero.
|
||||
* ERANGE semaphore count is too big
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
long waiters;
|
||||
sem_t s = *sem;
|
||||
|
||||
if (s == NULL || count <= 0)
|
||||
result = EINVAL;
|
||||
else if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
result = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->value <= (SEM_VALUE_MAX - count))
|
||||
{
|
||||
waiters = -s->value;
|
||||
s->value += count;
|
||||
if (waiters > 0)
|
||||
{
|
||||
|
||||
pte_osSemaphorePost(s->sem, (waiters<=count)?waiters:count);
|
||||
result = 0;
|
||||
}
|
||||
/*
|
||||
else
|
||||
{
|
||||
s->value -= count;
|
||||
result = EINVAL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ERANGE;
|
||||
}
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sem_timedwait (sem_t * sem, const struct timespec *abstime)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a semaphore possibly until
|
||||
* 'abstime' time.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* abstime
|
||||
* pointer to an instance of struct timespec
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* the calling thread (or process) is blocked until it can
|
||||
* successfully decrease the value or until interrupted by
|
||||
* a signal.
|
||||
*
|
||||
* If 'abstime' is a NULL pointer then this function will
|
||||
* block until it can successfully decrease the value or
|
||||
* until interrupted by a signal.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
* ETIMEDOUT abstime elapsed before success.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
if (sem == NULL)
|
||||
result = EINVAL;
|
||||
else
|
||||
{
|
||||
unsigned int milliseconds;
|
||||
unsigned int *pTimeout;
|
||||
|
||||
if (abstime == NULL)
|
||||
{
|
||||
pTimeout = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Calculate timeout as milliseconds from current system time.
|
||||
*/
|
||||
milliseconds = pte_relmillisecs (abstime);
|
||||
pTimeout = &milliseconds;
|
||||
}
|
||||
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = --s->value;
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
if (v < 0)
|
||||
{
|
||||
|
||||
{
|
||||
sem_timedwait_cleanup_args_t cleanup_args;
|
||||
|
||||
cleanup_args.sem = s;
|
||||
cleanup_args.resultPtr = &result;
|
||||
|
||||
/* Must wait */
|
||||
pthread_cleanup_push(pte_sem_timedwait_cleanup, (void *) &cleanup_args);
|
||||
|
||||
result = pte_cancellable_wait(s->sem,pTimeout);
|
||||
|
||||
pthread_cleanup_pop(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
|
||||
errno = result;
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_trywait (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function tries to wait on a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function tries to wait on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* this function returns immediately with the error EAGAIN
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EAGAIN the semaphore was already locked,
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOTSUP sem_trywait is not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
if (s == NULL)
|
||||
result = EINVAL;
|
||||
else if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->value > 0)
|
||||
s->value--;
|
||||
else
|
||||
result = EAGAIN;
|
||||
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sem_unlink (const char *name)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sem_wait (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a semaphore.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* the calling thread (or process) is blocked until it can
|
||||
* successfully decrease the value or until interrupted by
|
||||
* a signal.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = --s->value;
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
if (v < 0)
|
||||
{
|
||||
/* Must wait */
|
||||
pthread_cleanup_push(pte_sem_wait_cleanup, (void *) s);
|
||||
result = pte_cancellable_wait(s->sem,NULL);
|
||||
/* Cleanup if we're canceled or on any other error */
|
||||
pthread_cleanup_pop(result);
|
||||
|
||||
// Wait was cancelled, indicate that we're no longer waiting on this semaphore.
|
||||
/*
|
||||
if (result == PTE_OS_INTERRUPTED)
|
||||
{
|
||||
result = EINTR;
|
||||
++s->value;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
} /* sem_wait */
|
||||
|
||||
|
||||
int
|
||||
sem_wait_nocancel (sem_t * sem)
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* DOCPUBLIC
|
||||
* This function waits on a semaphore, and doesn't
|
||||
* allow cancellation.
|
||||
*
|
||||
* PARAMETERS
|
||||
* sem
|
||||
* pointer to an instance of sem_t
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function waits on a semaphore. If the
|
||||
* semaphore value is greater than zero, it decreases
|
||||
* its value by one. If the semaphore value is zero, then
|
||||
* the calling thread (or process) is blocked until it can
|
||||
* successfully decrease the value or until interrupted by
|
||||
* a signal.
|
||||
*
|
||||
* RESULTS
|
||||
* 0 successfully decreased semaphore,
|
||||
* -1 failed, error in errno
|
||||
* ERRNO
|
||||
* EINVAL 'sem' is not a valid semaphore,
|
||||
* ENOSYS semaphores are not supported,
|
||||
* EINTR the function was interrupted by a signal,
|
||||
* EDEADLK a deadlock condition was detected.
|
||||
*
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int result = 0;
|
||||
sem_t s = *sem;
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
result = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((result = pthread_mutex_lock (&s->lock)) == 0)
|
||||
{
|
||||
int v;
|
||||
|
||||
/* See sem_destroy.c
|
||||
*/
|
||||
if (*sem == NULL)
|
||||
{
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
v = --s->value;
|
||||
(void) pthread_mutex_unlock (&s->lock);
|
||||
|
||||
if (v < 0)
|
||||
{
|
||||
pte_osSemaphorePend(s->sem, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
errno = result;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
-114
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Module: semaphore.h
|
||||
*
|
||||
* Purpose:
|
||||
* Semaphores aren't actually part of the PThreads standard.
|
||||
* They are defined by the POSIX Standard:
|
||||
*
|
||||
* POSIX 1003.1b-1993 (POSIX.1b)
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
|
||||
* Copyright(C) 2008 Jason Schmidlapp
|
||||
*
|
||||
* Contact Email: jschmidlapp@users.sourceforge.net
|
||||
*
|
||||
*
|
||||
* Based upon Pthreads-win32 - POSIX Threads Library for Win32
|
||||
* Copyright(C) 1998 John E. Bossom
|
||||
* Copyright(C) 1999,2005 Pthreads-win32 contributors
|
||||
*
|
||||
* Contact Email: rpj@callisto.canberra.edu.au
|
||||
*
|
||||
* The original list of contributors to the Pthreads-win32 project
|
||||
* is contained in the file CONTRIBUTORS.ptw32 included with the
|
||||
* source code distribution. The list can also be seen at the
|
||||
* following World Wide Web location:
|
||||
* http://sources.redhat.com/pthreads-win32/contributors.html
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library in the file COPYING.LIB;
|
||||
* if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#if !defined( SEMAPHORE_H )
|
||||
#define SEMAPHORE_H
|
||||
|
||||
#if defined(_POSIX_SOURCE)
|
||||
#define PTE_LEVEL 0
|
||||
/* Early POSIX */
|
||||
#endif
|
||||
|
||||
#if defined(INCLUDE_NP)
|
||||
#undef PTE_LEVEL
|
||||
#define PTE_LEVEL 2
|
||||
/* Include Non-Portable extensions */
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#define _POSIX_SEMAPHORES
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
typedef struct sem_t_ * sem_t;
|
||||
|
||||
int sem_init (sem_t * sem,
|
||||
int pshared,
|
||||
unsigned int value);
|
||||
|
||||
int sem_destroy (sem_t * sem);
|
||||
|
||||
int sem_trywait (sem_t * sem);
|
||||
|
||||
int sem_wait (sem_t * sem);
|
||||
|
||||
int sem_timedwait (sem_t * sem,
|
||||
const struct timespec * abstime);
|
||||
|
||||
int sem_post (sem_t * sem);
|
||||
|
||||
int sem_post_multiple (sem_t * sem,
|
||||
int count);
|
||||
|
||||
int sem_open (const char * name,
|
||||
int oflag,
|
||||
mode_t mode,
|
||||
unsigned int value);
|
||||
|
||||
int sem_close (sem_t * sem);
|
||||
|
||||
int sem_unlink (const char * name);
|
||||
|
||||
int sem_getvalue (sem_t * sem,
|
||||
int * sval);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !SEMAPHORE_H */
|
||||
Vendored
+1
-1
@@ -120,7 +120,7 @@ extern "C" {
|
||||
rc_hash_cdreader_absolute_sector_to_track_sector absolute_sector_to_track_sector;
|
||||
};
|
||||
|
||||
void rc_hash_init_default_cdreader();
|
||||
void rc_hash_init_default_cdreader(void);
|
||||
void rc_hash_init_custom_cdreader(struct rc_hash_cdreader* reader);
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
Vendored
+6
-2
@@ -102,7 +102,7 @@ int rc_runtime_format_lboard_value(char* buffer, int size, int value, int format
|
||||
|
||||
|
||||
int rc_runtime_activate_richpresence(rc_runtime_t* runtime, const char* script, lua_State* L, int funcs_idx);
|
||||
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
|
||||
int rc_runtime_get_richpresence(const rc_runtime_t* runtime, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L);
|
||||
|
||||
enum {
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED, /* from WAITING, PAUSED, or PRIMED to ACTIVE */
|
||||
@@ -115,7 +115,8 @@ enum {
|
||||
RC_RUNTIME_EVENT_LBOARD_UPDATED,
|
||||
RC_RUNTIME_EVENT_LBOARD_TRIGGERED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED,
|
||||
RC_RUNTIME_EVENT_LBOARD_DISABLED
|
||||
RC_RUNTIME_EVENT_LBOARD_DISABLED,
|
||||
RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED
|
||||
};
|
||||
|
||||
typedef struct rc_runtime_event_t {
|
||||
@@ -129,6 +130,9 @@ typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_eve
|
||||
|
||||
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
|
||||
void rc_runtime_reset(rc_runtime_t* runtime);
|
||||
|
||||
typedef int (*rc_runtime_validate_address_t)(unsigned address);
|
||||
void rc_runtime_validate_addresses(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler);
|
||||
void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned address);
|
||||
|
||||
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);
|
||||
|
||||
+10
-2
@@ -239,10 +239,18 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
|
||||
|
||||
switch (self->oper) {
|
||||
case RC_OPERATOR_MULT:
|
||||
if (self->operand2.type == RC_OPERAND_FP)
|
||||
if (self->operand2.type == RC_OPERAND_FP) {
|
||||
value = (int)((double)value * self->operand2.value.dbl);
|
||||
else
|
||||
}
|
||||
else {
|
||||
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
|
||||
* to the type's size. this allows negative multiplication through twos-complements. i.e.
|
||||
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
|
||||
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
|
||||
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
|
||||
*/
|
||||
value *= rc_evaluate_operand(&self->operand2, eval_state);
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_DIV:
|
||||
|
||||
+9
-4
@@ -338,10 +338,14 @@ static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
|
||||
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
|
||||
|
||||
/* ===== Magnavox Odyssey 2 ===== */
|
||||
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
|
||||
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
|
||||
{ 0x000000U, 0x00003FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
/* Internal and external RAMs are reachable using unique instructions.
|
||||
* The real addresses provided are virtual and for mapping purposes only. */
|
||||
{ 0x000000U, 0x00003FU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Internal RAM" },
|
||||
{ 0x000040U, 0x00013FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "External RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 1 };
|
||||
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 2 };
|
||||
|
||||
/* ===== Master System ===== */
|
||||
/* http://www.smspower.org/Development/MemoryMap */
|
||||
@@ -374,8 +378,9 @@ static const rc_memory_regions_t rc_memory_regions_msx = { _rc_memory_regions_ms
|
||||
/* ===== Neo Geo Pocket ===== */
|
||||
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
|
||||
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
|
||||
/* MednafenNGP exposes 16KB, but the doc suggests there's 24-32KB */
|
||||
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
/* The docs suggest there's Work RAM exposed from $0000-$6FFF, Sound RAM from $7000-$7FFF, and Video
|
||||
* RAM from $8000-$BFFF, but both MednafenNGP and FBNeo only expose system RAM from $4000-$7FFF */
|
||||
{ 0x000000U, 0x003FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
|
||||
};
|
||||
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };
|
||||
|
||||
|
||||
Vendored
+89
-41
@@ -95,6 +95,86 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
|
||||
unsigned rc_transform_memref_value(unsigned value, char size)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (value >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (value >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (value >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (value >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (value >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (value >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (value >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (value >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = value & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (value >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
value = rc_bits_set[(value & 0x0F)]
|
||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
char rc_memref_shared_size(char size)
|
||||
{
|
||||
switch (size) {
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
case RC_MEMSIZE_LOW:
|
||||
case RC_MEMSIZE_HIGH:
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_transform_memref_value */
|
||||
return RC_MEMSIZE_8_BITS;
|
||||
|
||||
default:
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
|
||||
unsigned value;
|
||||
|
||||
@@ -103,46 +183,6 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (peek(address, 1, ud) >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (peek(address, 1, ud) >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (peek(address, 1, ud) >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (peek(address, 1, ud) >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (peek(address, 1, ud) >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (peek(address, 1, ud) >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (peek(address, 1, ud) >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (peek(address, 1, ud) >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = peek(address, 1, ud) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (peek(address, 1, ud) >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_8_BITS:
|
||||
value = peek(address, 1, ud);
|
||||
break;
|
||||
@@ -161,7 +201,15 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
|
||||
break;
|
||||
|
||||
default:
|
||||
value = 0;
|
||||
if (rc_memref_shared_size(size) == RC_MEMSIZE_8_BITS)
|
||||
{
|
||||
value = peek(address, 1, ud);
|
||||
value = rc_transform_memref_value(value, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
-73
@@ -102,27 +102,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
|
||||
if (ret != RC_OK)
|
||||
return ret;
|
||||
|
||||
switch (self->size) {
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
case RC_MEMSIZE_LOW:
|
||||
case RC_MEMSIZE_HIGH:
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_evaluate_operand */
|
||||
size = RC_MEMSIZE_8_BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
size = self->size;
|
||||
break;
|
||||
}
|
||||
|
||||
size = rc_memref_shared_size(self->size);
|
||||
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
|
||||
if (parse->offset < 0)
|
||||
return parse->offset;
|
||||
@@ -295,8 +275,6 @@ int rc_operand_is_memref(rc_operand_t* self) {
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
|
||||
|
||||
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
#ifndef RC_DISABLE_LUA
|
||||
rc_luapeek_t luapeek;
|
||||
@@ -348,56 +326,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
|
||||
}
|
||||
|
||||
/* step 2: mask off appropriate bits */
|
||||
switch (self->size)
|
||||
{
|
||||
case RC_MEMSIZE_BIT_0:
|
||||
value = (value >> 0) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_1:
|
||||
value = (value >> 1) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_2:
|
||||
value = (value >> 2) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_3:
|
||||
value = (value >> 3) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_4:
|
||||
value = (value >> 4) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_5:
|
||||
value = (value >> 5) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_6:
|
||||
value = (value >> 6) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BIT_7:
|
||||
value = (value >> 7) & 1;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_LOW:
|
||||
value = value & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_HIGH:
|
||||
value = (value >> 4) & 0x0f;
|
||||
break;
|
||||
|
||||
case RC_MEMSIZE_BITCOUNT:
|
||||
value = rc_bits_set[(value & 0x0F)]
|
||||
+ rc_bits_set[((value >> 4) & 0x0F)];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
value = rc_transform_memref_value(value, self->size);
|
||||
|
||||
/* step 3: apply logic */
|
||||
switch (self->type)
|
||||
|
||||
+3
@@ -117,8 +117,11 @@ void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
|
||||
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
|
||||
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
|
||||
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
|
||||
char rc_memref_shared_size(char size);
|
||||
unsigned rc_transform_memref_value(unsigned value, char size);
|
||||
|
||||
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
|
||||
int rc_trigger_state_active(int state);
|
||||
|
||||
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse, int is_value);
|
||||
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
|
||||
|
||||
+5
-5
@@ -131,7 +131,7 @@ static int rc_libretro_match_value(const char* val, const char* match) {
|
||||
if (*match == ',') {
|
||||
do {
|
||||
const char* ptr = ++match;
|
||||
int size;
|
||||
size_t size;
|
||||
|
||||
while (*match && *match != ',')
|
||||
++match;
|
||||
@@ -218,7 +218,7 @@ unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regio
|
||||
return ®ions->data[i][address];
|
||||
}
|
||||
|
||||
address -= size;
|
||||
address -= (unsigned)size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -360,8 +360,8 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X",
|
||||
(unsigned)(desc - mmap->descriptors) + 1, (int)offset);
|
||||
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X%s",
|
||||
(unsigned)(desc - mmap->descriptors) + 1, (int)offset, desc->ptr ? "" : " [no pointer]");
|
||||
|
||||
if (desc->ptr) {
|
||||
desc_start = (uint8_t*)desc->ptr + desc->offset;
|
||||
@@ -387,7 +387,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
|
||||
else {
|
||||
rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
|
||||
console_region_size -= desc_size;
|
||||
real_address += desc_size;
|
||||
real_address += (unsigned)desc_size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+3
-2
@@ -22,8 +22,9 @@ static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* mema
|
||||
if (rc_parse_memref(&end, &size, &address) == RC_OK) {
|
||||
/* make sure the entire memaddr was consumed. if not, there's an operator and it's a comparison, not a memory reference */
|
||||
if (end == &memaddr[memaddr_len]) {
|
||||
/* just a memory reference, allocate it */
|
||||
return &rc_alloc_memref(parse, address, size, 0)->value;
|
||||
/* if it's not a derived size, we can reference the memref directly */
|
||||
if (rc_memref_shared_size(size) == size)
|
||||
return &rc_alloc_memref(parse, address, size, 0)->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+124
-60
@@ -229,19 +229,13 @@ int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, unsigned id
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (trigger->state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_DISABLED:
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
/* don't report measured information for inactive triggers */
|
||||
*measured_value = *measured_target = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
*measured_value = trigger->measured_value;
|
||||
*measured_target = trigger->measured_target;
|
||||
break;
|
||||
if (rc_trigger_state_active(trigger->state)) {
|
||||
*measured_value = trigger->measured_value;
|
||||
*measured_target = trigger->measured_target;
|
||||
}
|
||||
else {
|
||||
/* don't report measured information for inactive triggers */
|
||||
*measured_value = *measured_target = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -457,7 +451,7 @@ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, lua
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned buffersize, rc_runtime_peek_t peek, void* peek_ud, lua_State* L) {
|
||||
if (self->richpresence && self->richpresence->richpresence)
|
||||
return rc_get_richpresence_display_string(self->richpresence->richpresence, buffer, buffersize, peek, peek_ud, L);
|
||||
|
||||
@@ -465,7 +459,7 @@ int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_peek_t peek, void* ud, lua_State* L) {
|
||||
void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L) {
|
||||
rc_runtime_event_t runtime_event;
|
||||
int i;
|
||||
|
||||
@@ -476,7 +470,7 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||
|
||||
for (i = self->trigger_count - 1; i >= 0; --i) {
|
||||
rc_trigger_t* trigger = self->triggers[i].trigger;
|
||||
int trigger_state;
|
||||
int old_state, new_state;
|
||||
|
||||
if (!trigger)
|
||||
continue;
|
||||
@@ -495,15 +489,34 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||
continue;
|
||||
}
|
||||
|
||||
trigger_state = trigger->state;
|
||||
switch (rc_evaluate_trigger(trigger, peek, ud, L))
|
||||
{
|
||||
case RC_TRIGGER_STATE_RESET:
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
break;
|
||||
old_state = trigger->state;
|
||||
new_state = rc_evaluate_trigger(trigger, peek, ud, L);
|
||||
|
||||
/* the trigger state doesn't actually change to RESET, RESET just serves as a notification.
|
||||
* handle the notification, then look at the actual state */
|
||||
if (new_state == RC_TRIGGER_STATE_RESET)
|
||||
{
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
|
||||
new_state = trigger->state;
|
||||
}
|
||||
|
||||
/* if the state hasn't changed, there won't be any events raised */
|
||||
if (new_state == old_state)
|
||||
continue;
|
||||
|
||||
/* raise an UNPRIMED event when changing from UNPRIMED to anything else */
|
||||
if (old_state == RC_TRIGGER_STATE_PRIMED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
|
||||
/* raise events for each of the possible new states */
|
||||
switch (new_state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
@@ -511,23 +524,21 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
|
||||
break;
|
||||
|
||||
case RC_TRIGGER_STATE_PAUSED:
|
||||
if (trigger_state != RC_TRIGGER_STATE_PAUSED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
break;
|
||||
|
||||
case RC_TRIGGER_STATE_PRIMED:
|
||||
if (trigger_state != RC_TRIGGER_STATE_PRIMED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
break;
|
||||
|
||||
case RC_TRIGGER_STATE_ACTIVE:
|
||||
if (trigger_state != RC_TRIGGER_STATE_ACTIVE) {
|
||||
/* only raise ACTIVATED event when transitioning from an inactive state.
|
||||
* note that inactive in this case means active but cannot trigger. */
|
||||
if (old_state == RC_TRIGGER_STATE_WAITING || old_state == RC_TRIGGER_STATE_PAUSED) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
event_handler(&runtime_event);
|
||||
@@ -667,32 +678,8 @@ static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memr
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
|
||||
static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref) {
|
||||
unsigned i;
|
||||
rc_memref_t* memref;
|
||||
rc_memref_t** last_memref;
|
||||
|
||||
if (!self->memrefs)
|
||||
return;
|
||||
|
||||
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
|
||||
* it's still there, so anything referencing it will continue to fetch 0.
|
||||
*/
|
||||
last_memref = &self->memrefs;
|
||||
memref = *last_memref;
|
||||
do {
|
||||
if (memref->address == address && !memref->value.is_indirect) {
|
||||
*last_memref = memref->next;
|
||||
break;
|
||||
}
|
||||
|
||||
last_memref = &memref->next;
|
||||
memref = *last_memref;
|
||||
} while (memref);
|
||||
|
||||
/* if the address is only used indirectly, don't disable anything dependent on it */
|
||||
if (!memref)
|
||||
return;
|
||||
|
||||
/* disable any achievements dependent on the address */
|
||||
for (i = 0; i < self->trigger_count; ++i) {
|
||||
@@ -726,3 +713,80 @@ void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
|
||||
rc_memref_t** last_memref = &self->memrefs;
|
||||
rc_memref_t* memref = self->memrefs;
|
||||
|
||||
while (memref) {
|
||||
if (memref->address == address && !memref->value.is_indirect) {
|
||||
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
|
||||
* it's still there, so anything referencing it will continue to fetch 0.
|
||||
*/
|
||||
*last_memref = memref->next;
|
||||
|
||||
rc_runtime_invalidate_memref(self, memref);
|
||||
break;
|
||||
}
|
||||
|
||||
last_memref = &memref->next;
|
||||
memref = *last_memref;
|
||||
}
|
||||
}
|
||||
|
||||
void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler,
|
||||
rc_runtime_validate_address_t validate_handler) {
|
||||
rc_memref_t** last_memref = &self->memrefs;
|
||||
rc_memref_t* memref = self->memrefs;
|
||||
int num_invalid = 0;
|
||||
|
||||
while (memref) {
|
||||
if (!memref->value.is_indirect && !validate_handler(memref->address)) {
|
||||
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
|
||||
* it's still there, so anything referencing it will continue to fetch 0.
|
||||
*/
|
||||
*last_memref = memref->next;
|
||||
|
||||
rc_runtime_invalidate_memref(self, memref);
|
||||
++num_invalid;
|
||||
}
|
||||
else {
|
||||
last_memref = &memref->next;
|
||||
}
|
||||
|
||||
memref = *last_memref;
|
||||
}
|
||||
|
||||
if (num_invalid) {
|
||||
rc_runtime_event_t runtime_event;
|
||||
int i;
|
||||
|
||||
for (i = self->trigger_count - 1; i >= 0; --i) {
|
||||
rc_trigger_t* trigger = self->triggers[i].trigger;
|
||||
if (trigger && self->triggers[i].invalid_memref) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
|
||||
runtime_event.id = self->triggers[i].id;
|
||||
runtime_event.value = self->triggers[i].invalid_memref->address;
|
||||
|
||||
trigger->state = RC_TRIGGER_STATE_DISABLED;
|
||||
self->triggers[i].invalid_memref = NULL;
|
||||
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = self->lboard_count - 1; i >= 0; --i) {
|
||||
rc_lboard_t* lboard = self->lboards[i].lboard;
|
||||
if (lboard && self->lboards[i].invalid_memref) {
|
||||
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
|
||||
runtime_event.id = self->lboards[i].id;
|
||||
runtime_event.value = self->lboards[i].invalid_memref->address;
|
||||
|
||||
lboard->state = RC_LBOARD_STATE_DISABLED;
|
||||
self->lboards[i].invalid_memref = NULL;
|
||||
|
||||
event_handler(&runtime_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-23
@@ -337,17 +337,9 @@ static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progres
|
||||
if (!runtime_trigger->trigger)
|
||||
continue;
|
||||
|
||||
switch (runtime_trigger->trigger->state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_DISABLED:
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
/* don't store state for inactive or triggered achievements */
|
||||
continue;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* don't store state for inactive or triggered achievements */
|
||||
if (!rc_trigger_state_active(runtime_trigger->trigger->state))
|
||||
continue;
|
||||
|
||||
if (!progress->buffer) {
|
||||
if (runtime_trigger->serialized_size) {
|
||||
@@ -470,19 +462,12 @@ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const unsigned char*
|
||||
for (i = 0; i < runtime->trigger_count; ++i) {
|
||||
rc_runtime_trigger_t* runtime_trigger = &runtime->triggers[i];
|
||||
if (runtime_trigger->trigger) {
|
||||
switch (runtime_trigger->trigger->state)
|
||||
/* don't update state for inactive or triggered achievements */
|
||||
if (rc_trigger_state_active(runtime_trigger->trigger->state))
|
||||
{
|
||||
case RC_TRIGGER_STATE_DISABLED:
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
/* don't update state for inactive or triggered achievements */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* mark active achievements as unupdated. anything that's still unupdated
|
||||
* after deserializing the progress will be reset to waiting */
|
||||
runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
|
||||
break;
|
||||
/* mark active achievements as unupdated. anything that's still unupdated
|
||||
* after deserializing the progress will be reset to waiting */
|
||||
runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+36
-10
@@ -80,6 +80,20 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
|
||||
return (parse.offset >= 0) ? self : NULL;
|
||||
}
|
||||
|
||||
int rc_trigger_state_active(int state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_DISABLED:
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc_reset_trigger_hitcounts(rc_trigger_t* self) {
|
||||
rc_condset_t* condset;
|
||||
|
||||
@@ -102,21 +116,28 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
char is_paused;
|
||||
char is_primed;
|
||||
|
||||
/* previously triggered, do nothing - return INACTIVE so caller doesn't think it triggered again */
|
||||
if (self->state == RC_TRIGGER_STATE_TRIGGERED)
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
switch (self->state)
|
||||
{
|
||||
case RC_TRIGGER_STATE_TRIGGERED:
|
||||
/* previously triggered. do nothing - return INACTIVE so caller doesn't think it triggered again */
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
|
||||
/* unsupported, do nothing - return INACTIVE */
|
||||
if (self->state == RC_TRIGGER_STATE_DISABLED)
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
case RC_TRIGGER_STATE_DISABLED:
|
||||
/* unsupported. do nothing - return INACTIVE */
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
|
||||
case RC_TRIGGER_STATE_INACTIVE:
|
||||
/* not yet active. update the memrefs so deltas are correct when it becomes active, then return INACTIVE */
|
||||
rc_update_memref_values(self->memrefs, peek, ud);
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* update the memory references */
|
||||
rc_update_memref_values(self->memrefs, peek, ud);
|
||||
|
||||
/* not yet active, only update the memrefs so deltas are correct when it becomes active */
|
||||
if (self->state == RC_TRIGGER_STATE_INACTIVE)
|
||||
return RC_TRIGGER_STATE_INACTIVE;
|
||||
|
||||
/* process the trigger */
|
||||
memset(&eval_state, 0, sizeof(eval_state));
|
||||
eval_state.peek = peek;
|
||||
@@ -178,6 +199,11 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State*
|
||||
/* if there were hit counts to clear, return RESET, but don't change the state */
|
||||
if (self->has_hits) {
|
||||
self->has_hits = 0;
|
||||
|
||||
/* cannot be PRIMED while ResetIf is true */
|
||||
if (self->state == RC_TRIGGER_STATE_PRIMED)
|
||||
self->state = RC_TRIGGER_STATE_ACTIVE;
|
||||
|
||||
return RC_TRIGGER_STATE_RESET;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
# What is Switchres 2.0
|
||||
Switchres is a modeline generation engine for emulation.
|
||||
|
||||
Its purpose is on-the-fly creation of fully customized video modes that accurately reproduce those of the emulated systems. Based on a monitor profile, it will provide the best video mode for a given width, height, and refresh rate.
|
||||
|
||||
Switchres features the most versatile modeline generation ever, ranging from 15-kHz low resolutions up to modern 8K, with full geometry control, smart scaling, refresh scaling, mode rotation, aspect ratio correction and much more.
|
||||
|
||||
Switchres can be integrated into open-source emulators either as a library, or used as a standalone emulator launcher. It's written in C++ and a C wrapper is also available.
|
||||
|
||||
Switchres 2.0 is a rewrite of the original Switchres code used in GroovyMAME. It currently supports mode switching on the following platforms, with their respective backends:
|
||||
- **Windows**:
|
||||
- AMD ADL (AMD Radeon HD 5000+)
|
||||
- ATI legacy (ATI Radeon pre-HD 5000)
|
||||
- PowerStrip (ATI, Nvidia, Matrox, etc., models up to 2012)
|
||||
- **Linux**:
|
||||
- X11/Xorg
|
||||
- KMS/DRM (WIP)
|
||||
|
||||
Each platform supports a different feature set, being X11/Xorg the most performant currently. In general, AMD video cards offer the best compatibility, and are a real requirement for the Windows platform.
|
||||
|
||||
# Using Switchres as a library
|
||||
If you are an emulator writer, you can integrate Switchres into your emulator in two ways:
|
||||
|
||||
- **Switchres shared library** (.dll or .so). This method offers a simplified way to add advanced mode switching features to your emulator, with minimal knowledge of Switchres internals.
|
||||
|
||||
- **Full Switchres integration**. If your emulator is written in C++, you can gain full access to Switchres' gears by including a Switchres manager class into your project, à la GroovyMAME.
|
||||
|
||||
Ask our devs for help and advice.
|
||||
|
||||
# Using Switchres standalone
|
||||
The standalone binary supports the following options:
|
||||
```
|
||||
Usage: switchres <width> <height> <refresh> [options]
|
||||
Options:
|
||||
-c, --calc Calculate video mode and exit
|
||||
-s, --switch Switch to video mode
|
||||
-l, --launch <command> Launch <command>
|
||||
-m, --monitor <preset> Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)
|
||||
-a --aspect <num:den> Monitor aspect ratio
|
||||
-r --rotated Original mode's native orientation is rotated
|
||||
-d, --display <OS_display_name> Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...)
|
||||
-f, --force <w>x<h>@<r> Force a specific video mode from display mode list
|
||||
-i, --ini <file.ini> Specify an ini file
|
||||
-b, --backend <api_name> Specify the api name
|
||||
-k, --keep Keep changes on exit (warning: this disables cleanup)
|
||||
```
|
||||
|
||||
A default `switchres.ini` file will be searched in the current working directory, then in `.\ini` on Windows, `./ini` then `/etc` on Linux. The repo has a switchres.ini example.
|
||||
|
||||
## Examples
|
||||
`switchres 320 240 60 --calc` will calculate and show a modeline for 320x240@60, computed using the current monitor preset in `switchres.ini`.
|
||||
|
||||
`switchres 320 240 60 -m ntsc -s` will switch your primary screen to 320x240 at 60Hz using the ntsc monitor model. Then it will wait until you press enter, and restore your initial screen resolution on exit.
|
||||
|
||||
`switchres 384 256 55.017605 -m arcade_15 -s -d \\.\DISPLAY1 -l "mame rtype"` will switch your display #1 to 384x256@55.017605 using the arcade_15 preset, then launch ``mame rtype``. Once mame has exited, it will restore the original resolution.
|
||||
|
||||
`switchres 640 480 57 -d 0 -m arcade_15 -d 1 -m arcade_31 -s` will set 640x480@57i (15-kHz preset) on your first display (index #0), 640x480@57p (31-kHz preset) on your second display (index #1)
|
||||
|
||||
# License
|
||||
GNU General Public License, version 2 or later (GPL-2.0+).
|
||||
Vendored
+178
@@ -0,0 +1,178 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video.cpp - Custom video library
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "custom_video.h"
|
||||
#include "log.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "custom_video_ati.h"
|
||||
#include "custom_video_adl.h"
|
||||
#include "custom_video_pstrip.h"
|
||||
#elif defined(__linux__)
|
||||
#ifdef SR_WITH_XRANDR
|
||||
#include "custom_video_xrandr.h"
|
||||
#endif
|
||||
#ifdef SR_WITH_KMSDRM
|
||||
#include "custom_video_drmkms.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
extern bool ati_is_legacy(int vendor, int device);
|
||||
|
||||
//============================================================
|
||||
// custom_video::make
|
||||
//============================================================
|
||||
|
||||
custom_video *custom_video::make(char *device_name, char *device_id, int method, custom_video_settings *vs)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
if (method == CUSTOM_VIDEO_TIMING_POWERSTRIP)
|
||||
{
|
||||
m_custom_video = new pstrip_timing(device_name, vs);
|
||||
if (m_custom_video)
|
||||
{
|
||||
m_custom_method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
|
||||
return m_custom_video;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int vendor, device;
|
||||
sscanf(device_id, "PCI\\VEN_%x&DEV_%x", &vendor, &device);
|
||||
|
||||
if (vendor == 0x1002) // ATI/AMD
|
||||
{
|
||||
if (ati_is_legacy(vendor, device))
|
||||
{
|
||||
m_custom_video = new ati_timing(device_name, vs);
|
||||
if (m_custom_video)
|
||||
{
|
||||
m_custom_method = CUSTOM_VIDEO_TIMING_ATI_LEGACY;
|
||||
return m_custom_video;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_custom_video = new adl_timing(device_name, vs);
|
||||
if (m_custom_video)
|
||||
{
|
||||
m_custom_method = CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||||
return m_custom_video;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
log_info("Video chipset is not compatible.\n");
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (device_id != NULL)
|
||||
log_info("Device value is %s.\n", device_id);
|
||||
|
||||
#ifdef SR_WITH_XRANDR
|
||||
if (method == CUSTOM_VIDEO_TIMING_XRANDR || method == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_custom_video = new xrandr_timing(device_name, vs);
|
||||
}
|
||||
catch (...) {};
|
||||
if (m_custom_video)
|
||||
{
|
||||
m_custom_method = CUSTOM_VIDEO_TIMING_XRANDR;
|
||||
return m_custom_video;
|
||||
}
|
||||
}
|
||||
#endif /* SR_WITH_XRANDR */
|
||||
|
||||
#ifdef SR_WITH_KMSDRM
|
||||
if (method == CUSTOM_VIDEO_TIMING_DRMKMS || method == 0)
|
||||
{
|
||||
m_custom_video = new drmkms_timing(device_name, vs);
|
||||
if (m_custom_video)
|
||||
{
|
||||
m_custom_method = CUSTOM_VIDEO_TIMING_DRMKMS;
|
||||
return m_custom_video;
|
||||
}
|
||||
}
|
||||
#endif /* SR_WITH_KMSDRM */
|
||||
#endif
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// custom_video::init
|
||||
//============================================================
|
||||
|
||||
bool custom_video::init() { return false; }
|
||||
|
||||
//============================================================
|
||||
// custom_video::get_timing
|
||||
//============================================================
|
||||
|
||||
bool custom_video::get_timing(modeline *mode)
|
||||
{
|
||||
log_verbose("system mode\n");
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_SYSTEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// custom_video::set_timing
|
||||
//============================================================
|
||||
|
||||
bool custom_video::set_timing(modeline *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// custom_video::add_mode
|
||||
//============================================================
|
||||
|
||||
bool custom_video::add_mode(modeline *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// custom_video::delete_mode
|
||||
//============================================================
|
||||
|
||||
bool custom_video::delete_mode(modeline *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// custom_video::update_mode
|
||||
//============================================================
|
||||
|
||||
bool custom_video::update_mode(modeline *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// custom_video::process_modelist
|
||||
//============================================================
|
||||
|
||||
bool custom_video::process_modelist(std::vector<modeline *>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vendored
+107
@@ -0,0 +1,107 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video.h - Custom video library header
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#ifndef __CUSTOM_VIDEO__
|
||||
#define __CUSTOM_VIDEO__
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include "modeline.h"
|
||||
|
||||
#define CUSTOM_VIDEO_TIMING_MASK 0x00000ff0
|
||||
#define CUSTOM_VIDEO_TIMING_AUTO 0x00000000
|
||||
#define CUSTOM_VIDEO_TIMING_SYSTEM 0x00000010
|
||||
#define CUSTOM_VIDEO_TIMING_XRANDR 0x00000020
|
||||
#define CUSTOM_VIDEO_TIMING_POWERSTRIP 0x00000040
|
||||
#define CUSTOM_VIDEO_TIMING_ATI_LEGACY 0x00000080
|
||||
#define CUSTOM_VIDEO_TIMING_ATI_ADL 0x00000100
|
||||
#define CUSTOM_VIDEO_TIMING_DRMKMS 0x00000200
|
||||
|
||||
// Custom video caps
|
||||
#define CUSTOM_VIDEO_CAPS_UPDATE 0x001
|
||||
#define CUSTOM_VIDEO_CAPS_ADD 0x002
|
||||
#define CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE 0x004
|
||||
#define CUSTOM_VIDEO_CAPS_SCAN_EDITABLE 0x008
|
||||
|
||||
// Timing creation commands
|
||||
#define TIMING_DELETE 0x001
|
||||
#define TIMING_CREATE 0x002
|
||||
#define TIMING_UPDATE 0x004
|
||||
#define TIMING_UPDATE_LIST 0x008
|
||||
|
||||
typedef struct custom_video_settings
|
||||
{
|
||||
bool screen_compositing;
|
||||
bool screen_reordering;
|
||||
bool allow_hardware_refresh;
|
||||
char device_reg_key[128];
|
||||
char custom_timing[256];
|
||||
} custom_video_settings;
|
||||
|
||||
class custom_video
|
||||
{
|
||||
public:
|
||||
|
||||
custom_video() {};
|
||||
virtual ~custom_video()
|
||||
{
|
||||
if (m_custom_video)
|
||||
{
|
||||
delete m_custom_video;
|
||||
m_custom_video = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
custom_video *make(char *device_name, char *device_id, int method, custom_video_settings *vs);
|
||||
virtual const char *api_name() { return "empty"; }
|
||||
virtual bool init();
|
||||
virtual int caps() { return 0; }
|
||||
|
||||
virtual bool add_mode(modeline *mode);
|
||||
virtual bool delete_mode(modeline *mode);
|
||||
virtual bool update_mode(modeline *mode);
|
||||
|
||||
virtual bool get_timing(modeline *mode);
|
||||
virtual bool set_timing(modeline *mode);
|
||||
|
||||
virtual bool process_modelist(std::vector<modeline *>);
|
||||
|
||||
// getters
|
||||
bool screen_compositing() { return m_vs.screen_compositing; }
|
||||
bool screen_reordering() { return m_vs.screen_reordering; }
|
||||
bool allow_hardware_refresh() { return m_vs.allow_hardware_refresh; }
|
||||
const char *custom_timing() { return (const char*) &m_vs.custom_timing; }
|
||||
|
||||
// setters
|
||||
void set_screen_compositing(bool value) { m_vs.screen_compositing = value; }
|
||||
void set_screen_reordering(bool value) { m_vs.screen_reordering = value; }
|
||||
void set_allow_hardware_refresh(bool value) { m_vs.allow_hardware_refresh = value; }
|
||||
void set_custom_timing(const char *custom_timing) { strncpy(m_vs.custom_timing, custom_timing, sizeof(m_vs.custom_timing)-1); }
|
||||
|
||||
// options
|
||||
custom_video_settings m_vs = {};
|
||||
|
||||
modeline m_user_mode = {};
|
||||
modeline m_backup_mode = {};
|
||||
|
||||
private:
|
||||
char m_device_name[32];
|
||||
char m_device_key[128];
|
||||
|
||||
custom_video *m_custom_video = 0;
|
||||
int m_custom_method;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+532
@@ -0,0 +1,532 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_adl.cpp - ATI/AMD ADL library
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
// Constants and structures ported from AMD ADL SDK files
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include "custom_video_adl.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
//============================================================
|
||||
// memory allocation callbacks
|
||||
//============================================================
|
||||
|
||||
void* __stdcall ADL_Main_Memory_Alloc(int iSize)
|
||||
{
|
||||
void* lpBuffer = malloc(iSize);
|
||||
return lpBuffer;
|
||||
}
|
||||
|
||||
void __stdcall ADL_Main_Memory_Free(void** lpBuffer)
|
||||
{
|
||||
if (NULL != *lpBuffer)
|
||||
{
|
||||
free(*lpBuffer);
|
||||
*lpBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::adl_timing
|
||||
//============================================================
|
||||
|
||||
adl_timing::adl_timing(char *display_name, custom_video_settings *vs)
|
||||
{
|
||||
m_vs = *vs;
|
||||
strcpy (m_display_name, display_name);
|
||||
strcpy (m_device_key, m_vs.device_reg_key);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::~adl_timing
|
||||
//============================================================
|
||||
|
||||
adl_timing::~adl_timing()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::init
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::init()
|
||||
{
|
||||
int ADL_Err = ADL_ERR;
|
||||
|
||||
log_verbose("ATI/AMD ADL init\n");
|
||||
|
||||
ADL_Err = open();
|
||||
if (ADL_Err != ADL_OK)
|
||||
{
|
||||
log_verbose("ERROR: ADL Initialization error!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ADL2_Adapter_NumberOfAdapters_Get = (ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_NumberOfAdapters_Get");
|
||||
if (ADL2_Adapter_NumberOfAdapters_Get == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Adapter_NumberOfAdapters_Get not available!");
|
||||
return false;
|
||||
}
|
||||
ADL2_Adapter_AdapterInfo_Get = (ADL2_ADAPTER_ADAPTERINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_AdapterInfo_Get");
|
||||
if (ADL2_Adapter_AdapterInfo_Get == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Adapter_AdapterInfo_Get not available!");
|
||||
return false;
|
||||
}
|
||||
ADL2_Display_DisplayInfo_Get = (ADL2_DISPLAY_DISPLAYINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_DisplayInfo_Get");
|
||||
if (ADL2_Display_DisplayInfo_Get == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Display_DisplayInfo_Get not available!");
|
||||
return false;
|
||||
}
|
||||
ADL2_Display_ModeTimingOverride_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Get");
|
||||
if (ADL2_Display_ModeTimingOverride_Get == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Get not available!");
|
||||
return false;
|
||||
}
|
||||
ADL2_Display_ModeTimingOverride_Set = (ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Set");
|
||||
if (ADL2_Display_ModeTimingOverride_Set == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Set not available!");
|
||||
return false;
|
||||
}
|
||||
ADL2_Display_ModeTimingOverrideList_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverrideList_Get");
|
||||
if (ADL2_Display_ModeTimingOverrideList_Get == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Display_ModeTimingOverrideList_Get not available!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ADL2_Flush_Driver_Data = (ADL2_FLUSH_DRIVER_DATA) (void *) GetProcAddress(hDLL,"ADL2_Flush_Driver_Data");
|
||||
if (ADL2_Flush_Driver_Data == NULL)
|
||||
{
|
||||
log_verbose("ERROR: ADL2_Flush_Driver_Data not available!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!enum_displays())
|
||||
{
|
||||
log_error("ADL error enumerating displays.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_device_mapping_from_display_name())
|
||||
{
|
||||
log_error("ADL error mapping display.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_driver_version(m_device_key))
|
||||
{
|
||||
log_error("ADL driver version unknown!.\n");
|
||||
}
|
||||
|
||||
if (!get_timing_list())
|
||||
{
|
||||
log_error("ADL error getting list of timing overrides.\n");
|
||||
}
|
||||
|
||||
log_verbose("ADL functions retrieved successfully.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::adl_open
|
||||
//============================================================
|
||||
|
||||
int adl_timing::open()
|
||||
{
|
||||
ADL2_MAIN_CONTROL_CREATE ADL2_Main_Control_Create;
|
||||
int ADL_Err = ADL_ERR;
|
||||
|
||||
hDLL = LoadLibraryA("atiadlxx.dll");
|
||||
if (hDLL == NULL) hDLL = LoadLibraryA("atiadlxy.dll");
|
||||
|
||||
if (hDLL != NULL)
|
||||
{
|
||||
ADL2_Main_Control_Create = (ADL2_MAIN_CONTROL_CREATE) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Create");
|
||||
if (ADL2_Main_Control_Create != NULL)
|
||||
ADL_Err = ADL2_Main_Control_Create(ADL_Main_Memory_Alloc, 1, &m_adl);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("ADL Library not found!\n");
|
||||
}
|
||||
|
||||
return ADL_Err;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::close
|
||||
//============================================================
|
||||
|
||||
void adl_timing::close()
|
||||
{
|
||||
ADL2_MAIN_CONTROL_DESTROY ADL2_Main_Control_Destroy;
|
||||
|
||||
log_verbose("ATI/AMD ADL close\n");
|
||||
|
||||
for (int i = 0; i <= iNumberAdapters - 1; i++)
|
||||
ADL_Main_Memory_Free((void **)&lpAdapter[i].m_display_list);
|
||||
|
||||
ADL_Main_Memory_Free((void **)&lpAdapterInfo);
|
||||
ADL_Main_Memory_Free((void **)&lpAdapter);
|
||||
|
||||
ADL2_Main_Control_Destroy = (ADL2_MAIN_CONTROL_DESTROY) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Destroy");
|
||||
if (ADL2_Main_Control_Destroy != NULL)
|
||||
ADL2_Main_Control_Destroy(m_adl);
|
||||
|
||||
FreeLibrary(hDLL);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::get_driver_version
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::get_driver_version(char *device_key)
|
||||
{
|
||||
HKEY hkey;
|
||||
bool found = false;
|
||||
|
||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, device_key, 0, KEY_READ , &hkey) == ERROR_SUCCESS)
|
||||
{
|
||||
BYTE cat_ver[32];
|
||||
DWORD length = sizeof(cat_ver);
|
||||
if ((RegQueryValueExA(hkey, "Catalyst_Version", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
|
||||
(RegQueryValueExA(hkey, "RadeonSoftwareVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
|
||||
(RegQueryValueExA(hkey, "DriverVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS))
|
||||
{
|
||||
found = true;
|
||||
is_patched = (RegQueryValueExA(hkey, "CalamityRelease", NULL, NULL, NULL, NULL) == ERROR_SUCCESS);
|
||||
sscanf((char *)cat_ver, "%d.%d", &cat_version, &sub_version);
|
||||
log_verbose("AMD driver version %d.%d%s\n", cat_version, sub_version, is_patched? "(patched)":"");
|
||||
}
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::enum_displays
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::enum_displays()
|
||||
{
|
||||
ADL2_Adapter_NumberOfAdapters_Get(m_adl, &iNumberAdapters);
|
||||
|
||||
lpAdapterInfo = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * iNumberAdapters);
|
||||
memset(lpAdapterInfo, '\0', sizeof(AdapterInfo) * iNumberAdapters);
|
||||
ADL2_Adapter_AdapterInfo_Get(m_adl, lpAdapterInfo, sizeof(AdapterInfo) * iNumberAdapters);
|
||||
|
||||
lpAdapter = (LPAdapterList)malloc(sizeof(AdapterList) * iNumberAdapters);
|
||||
for (int i = 0; i <= iNumberAdapters - 1; i++)
|
||||
{
|
||||
lpAdapter[i].m_index = lpAdapterInfo[i].iAdapterIndex;
|
||||
lpAdapter[i].m_bus = lpAdapterInfo[i].iBusNumber;
|
||||
memcpy(&lpAdapter[i].m_name, &lpAdapterInfo[i].strAdapterName, ADL_MAX_PATH);
|
||||
memcpy(&lpAdapter[i].m_display_name, &lpAdapterInfo[i].strDisplayName, ADL_MAX_PATH);
|
||||
lpAdapter[i].m_num_of_displays = 0;
|
||||
lpAdapter[i].m_display_list = 0;
|
||||
|
||||
// Only get display info from target adapter (this api is very slow!)
|
||||
if (!strcmp(lpAdapter[i].m_display_name, m_display_name))
|
||||
ADL2_Display_DisplayInfo_Get(m_adl, lpAdapter[i].m_index, &lpAdapter[i].m_num_of_displays, &lpAdapter[i].m_display_list, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::get_device_mapping_from_display_name
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::get_device_mapping_from_display_name()
|
||||
{
|
||||
for (int i = 0; i <= iNumberAdapters -1; i++)
|
||||
{
|
||||
if (!strcmp(m_display_name, lpAdapter[i].m_display_name))
|
||||
{
|
||||
ADLDisplayInfo *display_list;
|
||||
display_list = lpAdapter[i].m_display_list;
|
||||
|
||||
for (int j = 0; j <= lpAdapter[i].m_num_of_displays - 1; j++)
|
||||
{
|
||||
if (lpAdapter[i].m_index == display_list[j].displayID.iDisplayLogicalAdapterIndex)
|
||||
{
|
||||
m_adapter_index = lpAdapter[i].m_index;
|
||||
m_display_index = display_list[j].displayID.iDisplayLogicalIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::display_mode_info_to_modeline
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m)
|
||||
{
|
||||
if (dmi->sDetailedTiming.sHTotal == 0) return false;
|
||||
|
||||
ADLDetailedTiming dt;
|
||||
memcpy(&dt, &dmi->sDetailedTiming, sizeof(ADLDetailedTiming));
|
||||
|
||||
if (dt.sHTotal == 0) return false;
|
||||
|
||||
m->htotal = dt.sHTotal;
|
||||
m->hactive = dt.sHDisplay;
|
||||
m->hbegin = dt.sHSyncStart;
|
||||
m->hend = dt.sHSyncWidth + m->hbegin;
|
||||
m->vtotal = dt.sVTotal;
|
||||
m->vactive = dt.sVDisplay;
|
||||
m->vbegin = dt.sVSyncStart;
|
||||
m->vend = dt.sVSyncWidth + m->vbegin;
|
||||
m->interlace = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED)? 1 : 0;
|
||||
m->doublescan = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_DOUBLE_SCAN)? 1 : 0;
|
||||
m->hsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_H_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1);
|
||||
m->vsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_V_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1) ;
|
||||
m->pclock = dt.sPixelClock * 10000;
|
||||
|
||||
m->height = m->height? m->height : dmi->iPelsHeight;
|
||||
m->width = m->width? m->width : dmi->iPelsWidth;
|
||||
m->refresh = m->refresh? m->refresh : dmi->iRefreshRate / interlace_factor(m->interlace, 1);;
|
||||
m->hfreq = float(m->pclock / m->htotal);
|
||||
m->vfreq = float(m->hfreq / m->vtotal) * (m->interlace? 2 : 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::get_timing_list
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::get_timing_list()
|
||||
{
|
||||
if (ADL2_Display_ModeTimingOverrideList_Get(m_adl, m_adapter_index, m_display_index, MAX_MODELINES, adl_mode, &m_num_of_adl_modes) != ADL_OK) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::get_timing_from_cache
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::get_timing_from_cache(modeline *m)
|
||||
{
|
||||
ADLDisplayModeInfo *mode = 0;
|
||||
|
||||
for (int i = 0; i < m_num_of_adl_modes; i++)
|
||||
{
|
||||
mode = &adl_mode[i];
|
||||
if (mode->iPelsWidth == m->width && mode->iPelsHeight == m->height && mode->iRefreshRate == m->refresh)
|
||||
{
|
||||
if ((m->interlace) && !(mode->sDetailedTiming.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED))
|
||||
continue;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
found:
|
||||
if (display_mode_info_to_modeline(mode, m)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::get_timing
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::get_timing(modeline *m)
|
||||
{
|
||||
ADLDisplayMode mode_in;
|
||||
ADLDisplayModeInfo mode_info_out;
|
||||
modeline m_temp = *m;
|
||||
|
||||
//modeline to ADLDisplayMode
|
||||
mode_in.iPelsHeight = m->height;
|
||||
mode_in.iPelsWidth = m->width;
|
||||
mode_in.iBitsPerPel = 32;
|
||||
mode_in.iDisplayFrequency = m->refresh * interlace_factor(m->interlace, 1);
|
||||
|
||||
if (ADL2_Display_ModeTimingOverride_Get(m_adl, m_adapter_index, m_display_index, &mode_in, &mode_info_out) != ADL_OK) goto not_found;
|
||||
if (display_mode_info_to_modeline(&mode_info_out, &m_temp))
|
||||
{
|
||||
if (m_temp.interlace == m->interlace)
|
||||
{
|
||||
memcpy(m, &m_temp, sizeof(modeline));
|
||||
m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
not_found:
|
||||
|
||||
// Try to get timing from our cache (interlaced modes are not properly retrieved by ADL_Display_ModeTimingOverride_Get)
|
||||
if (get_timing_from_cache(m))
|
||||
{
|
||||
m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::set_timing
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::set_timing(modeline *m)
|
||||
{
|
||||
return set_timing_override(m, TIMING_UPDATE);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::set_timing_override
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::set_timing_override(modeline *m, int update_mode)
|
||||
{
|
||||
ADLDisplayModeInfo mode_info = {};
|
||||
ADLDetailedTiming *dt;
|
||||
modeline m_temp;
|
||||
|
||||
//modeline to ADLDisplayModeInfo
|
||||
mode_info.iTimingStandard = (update_mode & TIMING_DELETE)? ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT : ADL_DL_MODETIMING_STANDARD_CUSTOM;
|
||||
mode_info.iPossibleStandard = 0;
|
||||
mode_info.iRefreshRate = m->refresh * interlace_factor(m->interlace, 0);
|
||||
mode_info.iPelsWidth = m->width;
|
||||
mode_info.iPelsHeight = m->height;
|
||||
|
||||
//modeline to ADLDetailedTiming
|
||||
dt = &mode_info.sDetailedTiming;
|
||||
dt->sTimingFlags = (m->interlace? ADL_DL_TIMINGFLAG_INTERLACED : 0) |
|
||||
(m->doublescan? ADL_DL_TIMINGFLAG_DOUBLE_SCAN: 0) |
|
||||
(m->hsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_H_SYNC_POLARITY : 0) |
|
||||
(m->vsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_V_SYNC_POLARITY : 0);
|
||||
dt->sHTotal = m->htotal;
|
||||
dt->sHDisplay = m->hactive;
|
||||
dt->sHSyncStart = m->hbegin;
|
||||
dt->sHSyncWidth = m->hend - m->hbegin;
|
||||
dt->sVTotal = m->vtotal;
|
||||
dt->sVDisplay = m->vactive;
|
||||
dt->sVSyncStart = m->vbegin;
|
||||
dt->sVSyncWidth = m->vend - m->vbegin;
|
||||
dt->sPixelClock = m->pclock / 10000;
|
||||
dt->sHOverscanRight = 0;
|
||||
dt->sHOverscanLeft = 0;
|
||||
dt->sVOverscanBottom = 0;
|
||||
dt->sVOverscanTop = 0;
|
||||
|
||||
if (ADL2_Display_ModeTimingOverride_Set(m_adl, m_adapter_index, m_display_index, &mode_info, (update_mode & TIMING_UPDATE_LIST)? 1 : 0) != ADL_OK) return false;
|
||||
|
||||
//ADL2_Flush_Driver_Data(m_adl, m_adapter_index);
|
||||
|
||||
// read modeline to trigger timing refresh on modded drivers
|
||||
memcpy(&m_temp, m, sizeof(modeline));
|
||||
if (update_mode & TIMING_UPDATE) get_timing(&m_temp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::add_mode
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::add_mode(modeline *mode)
|
||||
{
|
||||
if (!set_timing_override(mode, TIMING_UPDATE_LIST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_resync.wait();
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::delete_mode
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::delete_mode(modeline *mode)
|
||||
{
|
||||
if (!set_timing_override(mode, TIMING_DELETE | TIMING_UPDATE_LIST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_resync.wait();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::update_mode
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::update_mode(modeline *mode)
|
||||
{
|
||||
bool refresh_required = !is_patched || (mode->type & MODE_DESKTOP);
|
||||
|
||||
if (!set_timing_override(mode, refresh_required? TIMING_UPDATE_LIST : TIMING_UPDATE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (refresh_required) m_resync.wait();
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::process_modelist
|
||||
//============================================================
|
||||
|
||||
bool adl_timing::process_modelist(std::vector<modeline *> modelist)
|
||||
{
|
||||
bool refresh_required = false;
|
||||
bool error = false;
|
||||
|
||||
for (auto &mode : modelist)
|
||||
{
|
||||
if (mode->type & MODE_DELETE || mode->type & MODE_ADD || (mode->type & MODE_UPDATE && (!is_patched || (mode->type & MODE_DESKTOP))))
|
||||
refresh_required = true;
|
||||
|
||||
bool is_last = (mode == modelist.back());
|
||||
|
||||
if (!set_timing_override(mode, (mode->type & MODE_DELETE? TIMING_DELETE : TIMING_UPDATE) | (is_last && refresh_required? TIMING_UPDATE_LIST : 0)))
|
||||
{
|
||||
mode->type |= MODE_ERROR;
|
||||
error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode->type &= ~MODE_ERROR;
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh_required) m_resync.wait();
|
||||
return !error;
|
||||
}
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_adl.h - ATI/AMD ADL library header
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
#include "custom_video.h"
|
||||
#include "resync_windows.h"
|
||||
|
||||
// Constants and structures ported from AMD ADL SDK files
|
||||
#define ADL_MAX_PATH 256
|
||||
#define ADL_OK 0
|
||||
#define ADL_ERR -1
|
||||
|
||||
//ADL_DETAILED_TIMING.sTimingFlags
|
||||
#define ADL_DL_TIMINGFLAG_DOUBLE_SCAN 0x0001
|
||||
#define ADL_DL_TIMINGFLAG_INTERLACED 0x0002
|
||||
#define ADL_DL_TIMINGFLAG_H_SYNC_POLARITY 0x0004
|
||||
#define ADL_DL_TIMINGFLAG_V_SYNC_POLARITY 0x0008
|
||||
|
||||
//ADL_DISPLAY_MODE_INFO.iTimingStandard
|
||||
#define ADL_DL_MODETIMING_STANDARD_CVT 0x00000001 // CVT Standard
|
||||
#define ADL_DL_MODETIMING_STANDARD_GTF 0x00000002 // GFT Standard
|
||||
#define ADL_DL_MODETIMING_STANDARD_DMT 0x00000004 // DMT Standard
|
||||
#define ADL_DL_MODETIMING_STANDARD_CUSTOM 0x00000008 // User-defined standard
|
||||
#define ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT 0x00000010 // Remove Mode from overriden list
|
||||
#define ADL_DL_MODETIMING_STANDARD_CVT_RB 0x00000020 // CVT-RB Standard
|
||||
|
||||
typedef struct AdapterInfo
|
||||
{
|
||||
int iSize;
|
||||
int iAdapterIndex;
|
||||
char strUDID[ADL_MAX_PATH];
|
||||
int iBusNumber;
|
||||
int iDeviceNumber;
|
||||
int iFunctionNumber;
|
||||
int iVendorID;
|
||||
char strAdapterName[ADL_MAX_PATH];
|
||||
char strDisplayName[ADL_MAX_PATH];
|
||||
int iPresent;
|
||||
int iExist;
|
||||
char strDriverPath[ADL_MAX_PATH];
|
||||
char strDriverPathExt[ADL_MAX_PATH];
|
||||
char strPNPString[ADL_MAX_PATH];
|
||||
int iOSDisplayIndex;
|
||||
} AdapterInfo, *LPAdapterInfo;
|
||||
|
||||
typedef struct ADLDisplayID
|
||||
{
|
||||
int iDisplayLogicalIndex;
|
||||
int iDisplayPhysicalIndex;
|
||||
int iDisplayLogicalAdapterIndex;
|
||||
int iDisplayPhysicalAdapterIndex;
|
||||
} ADLDisplayID, *LPADLDisplayID;
|
||||
|
||||
|
||||
typedef struct ADLDisplayInfo
|
||||
{
|
||||
ADLDisplayID displayID;
|
||||
int iDisplayControllerIndex;
|
||||
char strDisplayName[ADL_MAX_PATH];
|
||||
char strDisplayManufacturerName[ADL_MAX_PATH];
|
||||
int iDisplayType;
|
||||
int iDisplayOutputType;
|
||||
int iDisplayConnector;
|
||||
int iDisplayInfoMask;
|
||||
int iDisplayInfoValue;
|
||||
} ADLDisplayInfo, *LPADLDisplayInfo;
|
||||
|
||||
typedef struct ADLDisplayMode
|
||||
{
|
||||
int iPelsHeight;
|
||||
int iPelsWidth;
|
||||
int iBitsPerPel;
|
||||
int iDisplayFrequency;
|
||||
} ADLDisplayMode;
|
||||
|
||||
typedef struct ADLDetailedTiming
|
||||
{
|
||||
int iSize;
|
||||
short sTimingFlags;
|
||||
short sHTotal;
|
||||
short sHDisplay;
|
||||
short sHSyncStart;
|
||||
short sHSyncWidth;
|
||||
short sVTotal;
|
||||
short sVDisplay;
|
||||
short sVSyncStart;
|
||||
short sVSyncWidth;
|
||||
unsigned short sPixelClock;
|
||||
short sHOverscanRight;
|
||||
short sHOverscanLeft;
|
||||
short sVOverscanBottom;
|
||||
short sVOverscanTop;
|
||||
short sOverscan8B;
|
||||
short sOverscanGR;
|
||||
} ADLDetailedTiming;
|
||||
|
||||
typedef struct ADLDisplayModeInfo
|
||||
{
|
||||
int iTimingStandard;
|
||||
int iPossibleStandard;
|
||||
int iRefreshRate;
|
||||
int iPelsWidth;
|
||||
int iPelsHeight;
|
||||
ADLDetailedTiming sDetailedTiming;
|
||||
} ADLDisplayModeInfo;
|
||||
|
||||
typedef struct AdapterList
|
||||
{
|
||||
int m_index;
|
||||
int m_bus;
|
||||
char m_name[ADL_MAX_PATH];
|
||||
char m_display_name[ADL_MAX_PATH];
|
||||
int m_num_of_displays;
|
||||
ADLDisplayInfo *m_display_list;
|
||||
} AdapterList, *LPAdapterList;
|
||||
|
||||
|
||||
typedef void* ADL_CONTEXT_HANDLE;
|
||||
typedef void* (__stdcall *ADL_MAIN_MALLOC_CALLBACK)(int);
|
||||
typedef int (*ADL2_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int, ADL_CONTEXT_HANDLE *);
|
||||
typedef int (*ADL2_MAIN_CONTROL_DESTROY)(ADL_CONTEXT_HANDLE);
|
||||
typedef int (*ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (ADL_CONTEXT_HANDLE, int*);
|
||||
typedef int (*ADL2_ADAPTER_ADAPTERINFO_GET) (ADL_CONTEXT_HANDLE, LPAdapterInfo, int);
|
||||
typedef int (*ADL2_DISPLAY_DISPLAYINFO_GET) (ADL_CONTEXT_HANDLE, int, int *, ADLDisplayInfo **, int);
|
||||
typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, ADLDisplayMode *lpModeIn, ADLDisplayModeInfo *lpModeInfoOut);
|
||||
typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, ADLDisplayModeInfo *lpMode, int iForceUpdate);
|
||||
typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, int iMaxNumOfOverrides, ADLDisplayModeInfo *lpModeInfoList, int *lpNumOfOverrides);
|
||||
typedef int (*ADL2_FLUSH_DRIVER_DATA) (ADL_CONTEXT_HANDLE, int iAdapterIndex);
|
||||
|
||||
|
||||
class adl_timing : public custom_video
|
||||
{
|
||||
public:
|
||||
adl_timing(char *display_name, custom_video_settings *vs);
|
||||
~adl_timing();
|
||||
const char *api_name() { return "AMD ADL"; }
|
||||
bool init();
|
||||
void close();
|
||||
int caps() { return allow_hardware_refresh()? CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_ADD | CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE : is_patched? CUSTOM_VIDEO_CAPS_UPDATE : 0; }
|
||||
|
||||
bool add_mode(modeline *mode);
|
||||
bool delete_mode(modeline *mode);
|
||||
bool update_mode(modeline *mode);
|
||||
|
||||
bool get_timing(modeline *m);
|
||||
bool set_timing(modeline *m);
|
||||
|
||||
bool process_modelist(std::vector<modeline *>);
|
||||
|
||||
private:
|
||||
int open();
|
||||
bool get_driver_version(char *device_key);
|
||||
bool enum_displays();
|
||||
bool get_device_mapping_from_display_name();
|
||||
bool display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m);
|
||||
bool get_timing_list();
|
||||
bool get_timing_from_cache(modeline *m);
|
||||
bool set_timing_override(modeline *m, int update_mode);
|
||||
|
||||
char m_display_name[32];
|
||||
char m_device_key[128];
|
||||
|
||||
int m_adapter_index = 0;
|
||||
int m_display_index = 0;
|
||||
|
||||
ADL2_ADAPTER_NUMBEROFADAPTERS_GET ADL2_Adapter_NumberOfAdapters_Get;
|
||||
ADL2_ADAPTER_ADAPTERINFO_GET ADL2_Adapter_AdapterInfo_Get;
|
||||
ADL2_DISPLAY_DISPLAYINFO_GET ADL2_Display_DisplayInfo_Get;
|
||||
ADL2_DISPLAY_MODETIMINGOVERRIDE_GET ADL2_Display_ModeTimingOverride_Get;
|
||||
ADL2_DISPLAY_MODETIMINGOVERRIDE_SET ADL2_Display_ModeTimingOverride_Set;
|
||||
ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET ADL2_Display_ModeTimingOverrideList_Get;
|
||||
ADL2_FLUSH_DRIVER_DATA ADL2_Flush_Driver_Data;
|
||||
|
||||
HINSTANCE hDLL;
|
||||
LPAdapterInfo lpAdapterInfo = NULL;
|
||||
LPAdapterList lpAdapter = NULL;;
|
||||
int iNumberAdapters = 0;
|
||||
int cat_version = 0;
|
||||
int sub_version = 0;
|
||||
bool is_patched = false;
|
||||
|
||||
ADL_CONTEXT_HANDLE m_adl = 0;
|
||||
ADLDisplayModeInfo adl_mode[MAX_MODELINES];
|
||||
int m_num_of_adl_modes = 0;
|
||||
|
||||
resync_handler m_resync;
|
||||
|
||||
int invert_pol(bool on_read) { return ((cat_version <= 12) || (cat_version >= 15 && on_read)); }
|
||||
int interlace_factor(bool interlace, bool on_read) { return interlace && ((cat_version <= 12) || (cat_version >= 15 && on_read))? 2 : 1; }
|
||||
};
|
||||
Vendored
+343
@@ -0,0 +1,343 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_ati.cpp - ATI legacy library
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include "custom_video_ati.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
//============================================================
|
||||
// ati_timing::ati_timing
|
||||
//============================================================
|
||||
|
||||
ati_timing::ati_timing(char *device_name, custom_video_settings *vs)
|
||||
{
|
||||
m_vs = *vs;
|
||||
strcpy (m_device_name, device_name);
|
||||
strcpy (m_device_key, m_vs.device_reg_key);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ati_timing::ati_timing
|
||||
//============================================================
|
||||
|
||||
bool ati_timing::init()
|
||||
{
|
||||
log_verbose("ATI legacy init\n");
|
||||
|
||||
// Get Windows version
|
||||
win_version = os_version();
|
||||
|
||||
if (win_version > 5 && !is_elevated())
|
||||
{
|
||||
log_error("ATI legacy error: the program needs administrator rights.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ati_timing::get_timing
|
||||
//============================================================
|
||||
|
||||
bool ati_timing::get_timing(modeline *mode)
|
||||
{
|
||||
HKEY hKey;
|
||||
char lp_name[1024];
|
||||
char lp_data[68];
|
||||
DWORD length;
|
||||
bool found = false;
|
||||
int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
|
||||
int vfreq_incr = 0;
|
||||
|
||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
|
||||
{
|
||||
sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
|
||||
length = sizeof(lp_data);
|
||||
|
||||
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
|
||||
found = true;
|
||||
else if (win_version > 5 && mode->interlace)
|
||||
{
|
||||
vfreq_incr = 1;
|
||||
sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
|
||||
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
|
||||
found = true;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
mode->pclock = get_DWORD_BCD(36, lp_data) * 10000;
|
||||
mode->hactive = get_DWORD_BCD(8, lp_data);
|
||||
mode->hbegin = get_DWORD_BCD(12, lp_data);
|
||||
mode->hend = get_DWORD_BCD(16, lp_data) + mode->hbegin;
|
||||
mode->htotal = get_DWORD_BCD(4, lp_data);
|
||||
mode->vactive = get_DWORD_BCD(24, lp_data);
|
||||
mode->vbegin = get_DWORD_BCD(28, lp_data);
|
||||
mode->vend = get_DWORD_BCD(32, lp_data) + mode->vbegin;
|
||||
mode->vtotal = get_DWORD_BCD(20, lp_data);
|
||||
mode->interlace = (get_DWORD(0, lp_data) & CRTC_INTERLACED)?1:0;
|
||||
mode->hsync = (get_DWORD(0, lp_data) & CRTC_H_SYNC_POLARITY)?0:1;
|
||||
mode->vsync = (get_DWORD(0, lp_data) & CRTC_V_SYNC_POLARITY)?0:1;
|
||||
mode->hfreq = mode->pclock / mode->htotal;
|
||||
mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
|
||||
mode->refresh_label = refresh_label;
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
|
||||
|
||||
int checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
|
||||
- mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
|
||||
if (checksum != get_DWORD(64, lp_data))
|
||||
log_verbose("bad checksum! ");
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
return (found);
|
||||
}
|
||||
log_verbose("Failed opening registry entry for mode. ");
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ati_timing::set_timing
|
||||
//============================================================
|
||||
|
||||
bool ati_timing::set_timing(modeline *mode)
|
||||
{
|
||||
HKEY hKey;
|
||||
char lp_name[1024];
|
||||
char lp_data[68];
|
||||
long checksum;
|
||||
bool found = false;
|
||||
int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
|
||||
int vfreq_incr = 0;
|
||||
|
||||
memset(lp_data, 0, sizeof(lp_data));
|
||||
set_DWORD_BCD(lp_data, (int)mode->pclock/10000, 36);
|
||||
set_DWORD_BCD(lp_data, mode->hactive, 8);
|
||||
set_DWORD_BCD(lp_data, mode->hbegin, 12);
|
||||
set_DWORD_BCD(lp_data, mode->hend - mode->hbegin, 16);
|
||||
set_DWORD_BCD(lp_data, mode->htotal, 4);
|
||||
set_DWORD_BCD(lp_data, mode->vactive, 24);
|
||||
set_DWORD_BCD(lp_data, mode->vbegin, 28);
|
||||
set_DWORD_BCD(lp_data, mode->vend - mode->vbegin, 32);
|
||||
set_DWORD_BCD(lp_data, mode->vtotal, 20);
|
||||
set_DWORD(lp_data, (mode->interlace?CRTC_INTERLACED:0) | (mode->hsync?0:CRTC_H_SYNC_POLARITY) | (mode->vsync?0:CRTC_V_SYNC_POLARITY), 0);
|
||||
|
||||
checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
|
||||
- mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
|
||||
set_DWORD(lp_data, checksum, 64);
|
||||
|
||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
|
||||
{
|
||||
sprintf (lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
|
||||
|
||||
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
|
||||
found = true;
|
||||
else if (win_version > 5 && mode->interlace)
|
||||
{
|
||||
vfreq_incr = 1;
|
||||
sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
|
||||
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!(found && RegSetValueExA(hKey, lp_name, 0, REG_BINARY, (LPBYTE)lp_data, 68) == ERROR_SUCCESS))
|
||||
log_info("Failed saving registry entry %s\n", lp_name);
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return (found);
|
||||
}
|
||||
|
||||
log_info("Failed updating registry entry for mode.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ati_timing::update_mode
|
||||
//============================================================
|
||||
|
||||
bool ati_timing::update_mode(modeline *mode)
|
||||
{
|
||||
if (!set_timing(mode))
|
||||
return false;
|
||||
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
|
||||
|
||||
// ATI needs a call to EnumDisplaySettings to refresh timings
|
||||
refresh_timings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ati_refresh_timings
|
||||
//============================================================
|
||||
|
||||
void ati_timing::refresh_timings(void)
|
||||
{
|
||||
int iModeNum = 0;
|
||||
DEVMODEA lpDevMode;
|
||||
|
||||
memset(&lpDevMode, 0, sizeof(DEVMODEA));
|
||||
lpDevMode.dmSize = sizeof(DEVMODEA);
|
||||
|
||||
while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, 0) != 0)
|
||||
iModeNum++;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// adl_timing::process_modelist
|
||||
//============================================================
|
||||
|
||||
bool ati_timing::process_modelist(std::vector<modeline *> modelist)
|
||||
{
|
||||
bool error = false;
|
||||
|
||||
for (auto &mode : modelist)
|
||||
{
|
||||
if (!set_timing(mode))
|
||||
{
|
||||
mode->type |= MODE_ERROR;
|
||||
error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode->type &= ~MODE_ERROR;
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
|
||||
}
|
||||
}
|
||||
|
||||
refresh_timings();
|
||||
return !error;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// get_DWORD
|
||||
//============================================================
|
||||
|
||||
int ati_timing::get_DWORD(int i, char *lp_data)
|
||||
{
|
||||
char out[32] = "";
|
||||
UINT32 x;
|
||||
|
||||
sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
|
||||
sscanf(out, "%08X", &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// get_DWORD_BCD
|
||||
//============================================================
|
||||
|
||||
int ati_timing::get_DWORD_BCD(int i, char *lp_data)
|
||||
{
|
||||
char out[32] = "";
|
||||
UINT32 x;
|
||||
|
||||
sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
|
||||
sscanf(out, "%d", &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// set_DWORD
|
||||
//============================================================
|
||||
|
||||
void ati_timing::set_DWORD(char *data_string, UINT32 data_dword, int offset)
|
||||
{
|
||||
char *p_dword = (char*)&data_dword;
|
||||
|
||||
data_string[offset] = p_dword[3]&0xFF;
|
||||
data_string[offset+1] = p_dword[2]&0xFF;
|
||||
data_string[offset+2] = p_dword[1]&0xFF;
|
||||
data_string[offset+3] = p_dword[0]&0xFF;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// set_DWORD_BCD
|
||||
//============================================================
|
||||
|
||||
void ati_timing::set_DWORD_BCD(char *data_string, UINT32 data_dword, int offset)
|
||||
{
|
||||
if (data_dword < 100000000)
|
||||
{
|
||||
int low_word, high_word;
|
||||
int a, b, c, d;
|
||||
char out[32] = "";
|
||||
|
||||
low_word = data_dword % 10000;
|
||||
high_word = data_dword / 10000;
|
||||
|
||||
sprintf(out, "%d %d %d %d", high_word / 100, high_word % 100 , low_word / 100, low_word % 100);
|
||||
sscanf(out, "%02X %02X %02X %02X", &a, &b, &c, &d);
|
||||
|
||||
data_string[offset] = a;
|
||||
data_string[offset+1] = b;
|
||||
data_string[offset+2] = c;
|
||||
data_string[offset+3] = d;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// os_version
|
||||
//============================================================
|
||||
|
||||
int ati_timing::os_version(void)
|
||||
{
|
||||
OSVERSIONINFOA lpVersionInfo;
|
||||
|
||||
memset(&lpVersionInfo, 0, sizeof(OSVERSIONINFOA));
|
||||
lpVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
||||
GetVersionExA (&lpVersionInfo);
|
||||
|
||||
return lpVersionInfo.dwMajorVersion;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// is_elevated
|
||||
//============================================================
|
||||
|
||||
bool ati_timing::is_elevated()
|
||||
{
|
||||
HANDLE htoken;
|
||||
bool result = false;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken))
|
||||
return false;
|
||||
|
||||
TOKEN_ELEVATION te = {0};
|
||||
DWORD dw_return_length;
|
||||
|
||||
if (GetTokenInformation(htoken, TokenElevation, &te, sizeof(te), &dw_return_length))
|
||||
{
|
||||
if (te.TokenIsElevated)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(htoken);
|
||||
return (result);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// win_interlace_factor
|
||||
//============================================================
|
||||
|
||||
int ati_timing::win_interlace_factor(modeline *mode)
|
||||
{
|
||||
if (win_version > 5 && mode->interlace)
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_ati.h - ATI legacy library header
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
#include "custom_video.h"
|
||||
|
||||
#define CRTC_DOUBLE_SCAN 0x0001
|
||||
#define CRTC_INTERLACED 0x0002
|
||||
#define CRTC_H_SYNC_POLARITY 0x0004
|
||||
#define CRTC_V_SYNC_POLARITY 0x0008
|
||||
|
||||
class ati_timing : public custom_video
|
||||
{
|
||||
public:
|
||||
ati_timing(char *device_name, custom_video_settings *vs);
|
||||
~ati_timing() {};
|
||||
const char *api_name() { return "ATI Legacy"; }
|
||||
bool init();
|
||||
int caps() { return CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_SCAN_EDITABLE; }
|
||||
|
||||
bool update_mode(modeline *mode);
|
||||
|
||||
bool get_timing(modeline *mode);
|
||||
bool set_timing(modeline *mode);
|
||||
|
||||
bool process_modelist(std::vector<modeline *>);
|
||||
|
||||
private:
|
||||
void refresh_timings(void);
|
||||
|
||||
int get_DWORD(int i, char *lp_data);
|
||||
int get_DWORD_BCD(int i, char *lp_data);
|
||||
void set_DWORD(char *data_string, UINT32 data_word, int offset);
|
||||
void set_DWORD_BCD(char *data_string, UINT32 data_word, int offset);
|
||||
int os_version(void);
|
||||
bool is_elevated();
|
||||
int win_interlace_factor(modeline *mode);
|
||||
|
||||
char m_device_name[32];
|
||||
char m_device_key[256];
|
||||
int win_version;
|
||||
};
|
||||
+848
@@ -0,0 +1,848 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_ati_family.cpp - ATI/AMD Radeon family
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
/* Constants and structures ported from Linux open source drivers:
|
||||
drivers\gpu\drm\radeon\radeon.h
|
||||
drivers\gpu\drm\radeon\radeon_family.h
|
||||
include\drm\drm_pciids.h
|
||||
*/
|
||||
|
||||
#ifndef RADEON_FAMILY_H
|
||||
#define RADEON_FAMILY_H
|
||||
|
||||
struct pci_device_id
|
||||
{
|
||||
int vendor, device;
|
||||
int subvendor, subdevice;
|
||||
int _class, _class_mask;
|
||||
int driver_data;
|
||||
};
|
||||
|
||||
enum radeon_family
|
||||
{
|
||||
CHIP_R100 = 0,
|
||||
CHIP_RV100,
|
||||
CHIP_RS100,
|
||||
CHIP_RV200,
|
||||
CHIP_RS200,
|
||||
CHIP_R200,
|
||||
CHIP_RV250,
|
||||
CHIP_RS300,
|
||||
CHIP_RV280,
|
||||
CHIP_R300,
|
||||
CHIP_R350,
|
||||
CHIP_RV350,
|
||||
CHIP_RV380,
|
||||
CHIP_R420,
|
||||
CHIP_R423,
|
||||
CHIP_RV410,
|
||||
CHIP_RS400,
|
||||
CHIP_RS480,
|
||||
CHIP_RS600,
|
||||
CHIP_RS690,
|
||||
CHIP_RS740,
|
||||
CHIP_RV515,
|
||||
CHIP_R520,
|
||||
CHIP_RV530,
|
||||
CHIP_RV560,
|
||||
CHIP_RV570,
|
||||
CHIP_R580,
|
||||
CHIP_R600,
|
||||
CHIP_RV610,
|
||||
CHIP_RV630,
|
||||
CHIP_RV670,
|
||||
CHIP_RV620,
|
||||
CHIP_RV635,
|
||||
CHIP_RS780,
|
||||
CHIP_RS880,
|
||||
CHIP_RV770,
|
||||
CHIP_RV730,
|
||||
CHIP_RV710,
|
||||
CHIP_RV740,
|
||||
CHIP_CEDAR,
|
||||
CHIP_REDWOOD,
|
||||
CHIP_JUNIPER,
|
||||
CHIP_CYPRESS,
|
||||
CHIP_HEMLOCK,
|
||||
CHIP_PALM,
|
||||
CHIP_SUMO,
|
||||
CHIP_SUMO2,
|
||||
CHIP_BARTS,
|
||||
CHIP_TURKS,
|
||||
CHIP_CAICOS,
|
||||
CHIP_CAYMAN,
|
||||
CHIP_ARUBA,
|
||||
CHIP_TAHITI,
|
||||
CHIP_PITCAIRN,
|
||||
CHIP_VERDE,
|
||||
CHIP_OLAND,
|
||||
CHIP_HAINAN,
|
||||
CHIP_BONAIRE,
|
||||
CHIP_KAVERI,
|
||||
CHIP_KABINI,
|
||||
CHIP_HAWAII,
|
||||
CHIP_MULLINS,
|
||||
CHIP_LAST,
|
||||
};
|
||||
|
||||
enum radeon_chip_flags
|
||||
{
|
||||
RADEON_FAMILY_MASK = 0x0000ffffUL,
|
||||
RADEON_FLAGS_MASK = 0xffff0000UL,
|
||||
RADEON_IS_MOBILITY = 0x00010000UL,
|
||||
RADEON_IS_IGP = 0x00020000UL,
|
||||
RADEON_SINGLE_CRTC = 0x00040000UL,
|
||||
RADEON_IS_AGP = 0x00080000UL,
|
||||
RADEON_HAS_HIERZ = 0x00100000UL,
|
||||
RADEON_IS_PCIE = 0x00200000UL,
|
||||
RADEON_NEW_MEMMAP = 0x00400000UL,
|
||||
RADEON_IS_PCI = 0x00800000UL,
|
||||
RADEON_IS_IGPGART = 0x01000000UL,
|
||||
RADEON_IS_PX = 0x02000000UL,
|
||||
};
|
||||
|
||||
#define PCI_ANY_ID (~0)
|
||||
|
||||
#define radeon_PCI_IDS \
|
||||
{0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x3151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x3155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x4149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x414A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x414B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x4150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
|
||||
{0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
|
||||
{0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
|
||||
{0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
|
||||
{0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4B48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4C6E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4E47, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
|
||||
{0x1002, 0x4E48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
|
||||
{0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
|
||||
{0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
|
||||
{0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
|
||||
{0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
|
||||
{0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
|
||||
{0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
|
||||
{0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
|
||||
{0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
|
||||
{0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
|
||||
{0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
|
||||
{0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
|
||||
{0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
|
||||
{0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5657, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5954, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5955, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5974, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
|
||||
{0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
|
||||
{0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
|
||||
{0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
|
||||
{0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
|
||||
{0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
|
||||
{0x1002, 0x5a41, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5a42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5a61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5a62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
|
||||
{0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6665, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6667, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x666F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6703, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6704, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6707, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6718, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6719, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x671c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x671d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x671f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6721, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6722, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6725, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6726, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6728, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6743, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6745, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6746, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6747, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6748, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x674A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6758, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6759, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x675B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x675D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x675F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6761, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6763, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6764, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6765, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6766, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6767, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6768, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6770, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6771, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6778, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6779, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x677B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6784, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x678A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6790, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6791, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6792, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6798, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6799, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x679A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6823, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6826, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6827, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x683B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x683D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x683F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6842, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6843, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6849, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x684C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6888, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6889, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x688A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x688C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68a0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68e1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68e5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68e8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68e9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68f1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68f8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68f9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68fa, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x68fe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x710A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x710B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x710C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x710E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x710F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7140, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7141, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7142, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7143, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x714A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x714B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x714C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x714D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x714E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x714F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x715E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x715F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7183, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7187, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7188, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x718A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x718B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x718C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x718D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x718F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7193, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x719B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x719F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71D2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71D5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71D6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x71DE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7244, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7245, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7247, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7248, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x724A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x724B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x724C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x724D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x724E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x724F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7280, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7283, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7284, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x728B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x728C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7290, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7291, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7293, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7297, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x791e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x791f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x793f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7941, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x7942, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x796c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x796d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x796e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x796f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
|
||||
{0x1002, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9402, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9403, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x940A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x940B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x940F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94B3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94B5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9442, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9444, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x944A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x944B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x944C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x944E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9452, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9456, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x945A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x945B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x945E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x946A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x946B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x947A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x947B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9487, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9488, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9489, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x948A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x948F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9490, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9491, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9495, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9498, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x949C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x949E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x949F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94CB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x94CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9504, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9505, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9506, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9507, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9508, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9509, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x950F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9517, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9540, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9542, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x954E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x954F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9553, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9555, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x955f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9580, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9581, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9583, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9586, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9587, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9588, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9589, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x958A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x958B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x958C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x958D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x958E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x958F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9590, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9593, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9595, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9596, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9597, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9599, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x959B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x95CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
|
||||
{0x1002, 0x9610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9616, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9642, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
|
||||
{0x1002, 0x9648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
|
||||
{0x1002, 0x9649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
|
||||
{0x1002, 0x964a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x964b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x964c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x964e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
|
||||
{0x1002, 0x964f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
|
||||
{0x1002, 0x9710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9714, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9715, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9804, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9807, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x980A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9904, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9905, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9906, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9907, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9908, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9909, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x990A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x990B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x990C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x990D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x990E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x990F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9910, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9913, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9917, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9918, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9990, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9991, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9993, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9994, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9996, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9998, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x9999, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x999A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x999B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x999C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x999D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x99A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x99A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0x1002, 0x99A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
|
||||
{0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
static struct pci_device_id pciidlist[] = {radeon_PCI_IDS};
|
||||
|
||||
//============================================================
|
||||
// ati_family
|
||||
//============================================================
|
||||
|
||||
int ati_family(int vendor, int device)
|
||||
{
|
||||
int i = 0;
|
||||
while (pciidlist[i].vendor)
|
||||
{
|
||||
if (pciidlist[i].vendor == vendor && pciidlist[i].device == device)
|
||||
return (pciidlist[i].driver_data & RADEON_FAMILY_MASK);
|
||||
i++;
|
||||
}
|
||||
// Not found, must be newer
|
||||
if (vendor == 0x1002)
|
||||
return CHIP_LAST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ati_is_legacy
|
||||
//============================================================
|
||||
|
||||
bool ati_is_legacy(int vendor, int device)
|
||||
{
|
||||
return (ati_family(vendor, device) < CHIP_CEDAR);
|
||||
}
|
||||
|
||||
#endif
|
||||
+795
@@ -0,0 +1,795 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_drmkms.cpp - Linux DRM/KMS video management layer
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include "custom_video_drmkms.h"
|
||||
#include "log.h"
|
||||
|
||||
#define drmGetVersion p_drmGetVersion
|
||||
#define drmFreeVersion p_drmFreeVersion
|
||||
#define drmModeGetResources p_drmModeGetResources
|
||||
#define drmModeGetConnector p_drmModeGetConnector
|
||||
#define drmModeFreeConnector p_drmModeFreeConnector
|
||||
#define drmModeFreeResources p_drmModeFreeResources
|
||||
#define drmModeGetEncoder p_drmModeGetEncoder
|
||||
#define drmModeFreeEncoder p_drmModeFreeEncoder
|
||||
#define drmModeGetCrtc p_drmModeGetCrtc
|
||||
#define drmModeSetCrtc p_drmModeSetCrtc
|
||||
#define drmModeFreeCrtc p_drmModeFreeCrtc
|
||||
#define drmModeAttachMode p_drmModeAttachMode
|
||||
#define drmModeAddFB p_drmModeAddFB
|
||||
#define drmModeRmFB p_drmModeRmFB
|
||||
#define drmModeGetFB p_drmModeGetFB
|
||||
#define drmModeFreeFB p_drmModeFreeFB
|
||||
#define drmPrimeHandleToFD p_drmPrimeHandleToFD
|
||||
#define drmModeGetPlaneResources p_drmModeGetPlaneResources
|
||||
#define drmModeFreePlaneResources p_drmModeFreePlaneResources
|
||||
#define drmIoctl p_drmIoctl
|
||||
#define drmGetCap p_drmGetCap
|
||||
#define drmIsMaster p_drmIsMaster
|
||||
#define drmSetMaster p_drmSetMaster
|
||||
#define drmDropMaster p_drmDropMaster
|
||||
|
||||
//============================================================
|
||||
// shared the privileges of the master fd
|
||||
//============================================================
|
||||
|
||||
static int s_shared_fd[10] = {};
|
||||
static int s_shared_count[10] = {};
|
||||
|
||||
//============================================================
|
||||
// list connector types
|
||||
//============================================================
|
||||
|
||||
const char *get_connector_name(int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case DRM_MODE_CONNECTOR_Unknown:
|
||||
return "Unknown";
|
||||
case DRM_MODE_CONNECTOR_VGA:
|
||||
return "VGA-";
|
||||
case DRM_MODE_CONNECTOR_DVII:
|
||||
return "DVI-I-";
|
||||
case DRM_MODE_CONNECTOR_DVID:
|
||||
return "DVI-D-";
|
||||
case DRM_MODE_CONNECTOR_DVIA:
|
||||
return "DVI-A-";
|
||||
case DRM_MODE_CONNECTOR_Composite:
|
||||
return "Composite-";
|
||||
case DRM_MODE_CONNECTOR_SVIDEO:
|
||||
return "SVIDEO-";
|
||||
case DRM_MODE_CONNECTOR_LVDS:
|
||||
return "LVDS-";
|
||||
case DRM_MODE_CONNECTOR_Component:
|
||||
return "Component-";
|
||||
case DRM_MODE_CONNECTOR_9PinDIN:
|
||||
return "9PinDIN-";
|
||||
case DRM_MODE_CONNECTOR_DisplayPort:
|
||||
return "DisplayPort-";
|
||||
case DRM_MODE_CONNECTOR_HDMIA:
|
||||
return "HDMI-A-";
|
||||
case DRM_MODE_CONNECTOR_HDMIB:
|
||||
return "HDMI-B-";
|
||||
case DRM_MODE_CONNECTOR_TV:
|
||||
return "TV-";
|
||||
case DRM_MODE_CONNECTOR_eDP:
|
||||
return "eDP-";
|
||||
case DRM_MODE_CONNECTOR_VIRTUAL:
|
||||
return "VIRTUAL-";
|
||||
case DRM_MODE_CONNECTOR_DSI:
|
||||
return "DSI-";
|
||||
case DRM_MODE_CONNECTOR_DPI:
|
||||
return "DPI-";
|
||||
default:
|
||||
return "not_defined-";
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// id for class object (static)
|
||||
//============================================================
|
||||
|
||||
static int static_id = 0;
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::drmkms_timing
|
||||
//============================================================
|
||||
|
||||
drmkms_timing::drmkms_timing(char *device_name, custom_video_settings *vs)
|
||||
{
|
||||
m_vs = *vs;
|
||||
m_id = ++static_id;
|
||||
|
||||
log_verbose("DRM/KMS: <%d> (drmkms_timing) creation (%s)\n", m_id, device_name);
|
||||
// Copy screen device name and limit size
|
||||
if ((strlen(device_name) + 1) > 32)
|
||||
{
|
||||
strncpy(m_device_name, device_name, 31);
|
||||
log_error("DRM/KMS: <%d> (drmkms_timing) [ERROR] the devine name is too long it has been trucated to %s\n", m_id, m_device_name);
|
||||
}
|
||||
else
|
||||
strcpy(m_device_name, device_name);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::~drmkms_timing
|
||||
//============================================================
|
||||
|
||||
drmkms_timing::~drmkms_timing()
|
||||
{
|
||||
// close DRM/KMS library
|
||||
if (mp_drm_handle)
|
||||
dlclose(mp_drm_handle);
|
||||
|
||||
if (m_drm_fd > 0)
|
||||
{
|
||||
if (!--s_shared_count[m_card_id])
|
||||
close(m_drm_fd);
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::init
|
||||
//============================================================
|
||||
|
||||
bool drmkms_timing::init()
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (init) loading DRM/KMS library\n", m_id);
|
||||
mp_drm_handle = dlopen("libdrm.so", RTLD_NOW);
|
||||
if (mp_drm_handle)
|
||||
{
|
||||
p_drmGetVersion = (__typeof__(drmGetVersion)) dlsym(mp_drm_handle, "drmGetVersion");
|
||||
if (p_drmGetVersion == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetVersion", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmFreeVersion = (__typeof__(drmFreeVersion)) dlsym(mp_drm_handle, "drmFreeVersion");
|
||||
if (p_drmFreeVersion == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmFreeVersion", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeGetResources = (__typeof__(drmModeGetResources)) dlsym(mp_drm_handle, "drmModeGetResources");
|
||||
if (p_drmModeGetResources == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetResources", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeGetConnector = (__typeof__(drmModeGetConnector)) dlsym(mp_drm_handle, "drmModeGetConnector");
|
||||
if (p_drmModeGetConnector == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetConnector", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeFreeConnector = (__typeof__(drmModeFreeConnector)) dlsym(mp_drm_handle, "drmModeFreeConnector");
|
||||
if (p_drmModeFreeConnector == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeConnector", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeFreeResources = (__typeof__(drmModeFreeResources)) dlsym(mp_drm_handle, "drmModeFreeResources");
|
||||
if (p_drmModeFreeResources == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeResources", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeGetEncoder = (__typeof__(drmModeGetEncoder)) dlsym(mp_drm_handle, "drmModeGetEncoder");
|
||||
if (p_drmModeGetEncoder == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetEncoder", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeFreeEncoder = (__typeof__(drmModeFreeEncoder)) dlsym(mp_drm_handle, "drmModeFreeEncoder");
|
||||
if (p_drmModeFreeEncoder == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeEncoder", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeGetCrtc = (__typeof__(drmModeGetCrtc)) dlsym(mp_drm_handle, "drmModeGetCrtc");
|
||||
if (p_drmModeGetCrtc == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetCrtc", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeSetCrtc = (__typeof__(drmModeSetCrtc)) dlsym(mp_drm_handle, "drmModeSetCrtc");
|
||||
if (p_drmModeSetCrtc == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeSetCrtc", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeFreeCrtc = (__typeof__(drmModeFreeCrtc)) dlsym(mp_drm_handle, "drmModeFreeCrtc");
|
||||
if (p_drmModeFreeCrtc == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeCrtc", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeAttachMode = (__typeof__(drmModeAttachMode)) dlsym(mp_drm_handle, "drmModeAttachMode");
|
||||
if (p_drmModeAttachMode == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeAttachMode", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeAddFB = (__typeof__(drmModeAddFB)) dlsym(mp_drm_handle, "drmModeAddFB");
|
||||
if (p_drmModeAddFB == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeAddFB", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeRmFB = (__typeof__(drmModeRmFB)) dlsym(mp_drm_handle, "drmModeRmFB");
|
||||
if (p_drmModeRmFB == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeRmFB", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeGetFB = (__typeof__(drmModeGetFB)) dlsym(mp_drm_handle, "drmModeGetFB");
|
||||
if (p_drmModeGetFB == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetFB", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeFreeFB = (__typeof__(drmModeFreeFB)) dlsym(mp_drm_handle, "drmModeFreeFB");
|
||||
if (p_drmModeFreeFB == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeFB", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmPrimeHandleToFD = (__typeof__(drmPrimeHandleToFD)) dlsym(mp_drm_handle, "drmPrimeHandleToFD");
|
||||
if (p_drmPrimeHandleToFD == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmPrimeHandleToFD", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeGetPlaneResources = (__typeof__(drmModeGetPlaneResources)) dlsym(mp_drm_handle, "drmModeGetPlaneResources");
|
||||
if (p_drmModeGetPlaneResources == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetPlaneResources", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmModeFreePlaneResources = (__typeof__(drmModeFreePlaneResources)) dlsym(mp_drm_handle, "drmModeFreePlaneResources");
|
||||
if (p_drmModeFreePlaneResources == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreePlaneResources", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmIoctl = (__typeof__(drmIoctl)) dlsym(mp_drm_handle, "drmIoctl");
|
||||
if (p_drmIoctl == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmIoctl", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmGetCap = (__typeof__(drmGetCap)) dlsym(mp_drm_handle, "drmGetCap");
|
||||
if (p_drmGetCap == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetCap", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmIsMaster = (__typeof__(drmIsMaster)) dlsym(mp_drm_handle, "drmIsMaster");
|
||||
if (p_drmIsMaster == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmIsMaster", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmSetMaster = (__typeof__(drmSetMaster)) dlsym(mp_drm_handle, "drmSetMaster");
|
||||
if (p_drmSetMaster == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmSetMaster", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
p_drmDropMaster = (__typeof__(drmDropMaster)) dlsym(mp_drm_handle, "drmDropMaster");
|
||||
if (p_drmDropMaster == NULL)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmDropMaster", "DRM_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] missing %s library\n", m_id, "DRM/KMS_LIBRARY");
|
||||
return false;
|
||||
}
|
||||
|
||||
int screen_pos = -1;
|
||||
|
||||
// Handle the screen name, "auto", "screen[0-9]" and device name
|
||||
if (strlen(m_device_name) == 7 && !strncmp(m_device_name, "screen", 6) && m_device_name[6] >= '0' && m_device_name[6] <= '9')
|
||||
screen_pos = m_device_name[6] - '0';
|
||||
else if (strlen(m_device_name) == 1 && m_device_name[0] >= '0' && m_device_name[0] <= '9')
|
||||
screen_pos = m_device_name[0] - '0';
|
||||
|
||||
char drm_name[15] = "/dev/dri/card_";
|
||||
drmModeRes *p_res;
|
||||
drmModeConnector *p_connector;
|
||||
|
||||
int output_position = 0;
|
||||
for (int num = 0; !m_desktop_output && num < 10; num++)
|
||||
{
|
||||
drm_name[13] = '0' + num;
|
||||
m_drm_fd = open(drm_name, O_RDWR | O_CLOEXEC);
|
||||
|
||||
if (m_drm_fd > 0)
|
||||
{
|
||||
drmVersion *version = drmGetVersion(m_drm_fd);
|
||||
log_verbose("DRM/KMS: <%d> (init) version %d.%d.%d type %s\n", m_id, version->version_major, version->version_minor, version->version_patchlevel, version->name);
|
||||
drmFreeVersion(version);
|
||||
|
||||
uint64_t check_dumb = 0;
|
||||
if (drmGetCap(m_drm_fd, DRM_CAP_DUMB_BUFFER, &check_dumb) < 0)
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] ioctl DRM_CAP_DUMB_BUFFER\n", m_id);
|
||||
|
||||
if (!check_dumb)
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] dumb buffer not supported\n", m_id);
|
||||
|
||||
p_res = drmModeGetResources(m_drm_fd);
|
||||
|
||||
for (int i = 0; i < p_res->count_connectors; i++)
|
||||
{
|
||||
p_connector = drmModeGetConnector(m_drm_fd, p_res->connectors[i]);
|
||||
if (p_connector)
|
||||
{
|
||||
char connector_name[32];
|
||||
snprintf(connector_name, 32, "%s%d", get_connector_name(p_connector->connector_type), p_connector->connector_type_id);
|
||||
log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s status %d - modes %d\n", m_id, num, i, p_connector->connector_id, connector_name, p_connector->connection, p_connector->count_modes);
|
||||
// detect desktop connector
|
||||
if (!m_desktop_output && p_connector->connection == DRM_MODE_CONNECTED)
|
||||
{
|
||||
if (!strcmp(m_device_name, "auto") || !strcmp(m_device_name, connector_name) || output_position == screen_pos)
|
||||
{
|
||||
m_desktop_output = p_connector->connector_id;
|
||||
m_card_id = num;
|
||||
log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s selected as primary output\n", m_id, num, i, m_desktop_output, connector_name);
|
||||
|
||||
drmModeEncoder *p_encoder = drmModeGetEncoder(m_drm_fd, p_connector->encoder_id);
|
||||
|
||||
if (p_encoder)
|
||||
{
|
||||
for (int e = 0; e < p_res->count_crtcs; e++)
|
||||
{
|
||||
mp_crtc_desktop = drmModeGetCrtc(m_drm_fd, p_res->crtcs[e]);
|
||||
|
||||
if (mp_crtc_desktop->crtc_id == p_encoder->crtc_id)
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (init) desktop mode name %s crtc %d fb %d valid %d\n", m_id, mp_crtc_desktop->mode.name, mp_crtc_desktop->crtc_id, mp_crtc_desktop->buffer_id, mp_crtc_desktop->mode_valid);
|
||||
break;
|
||||
}
|
||||
drmModeFreeCrtc(mp_crtc_desktop);
|
||||
}
|
||||
}
|
||||
if (!mp_crtc_desktop)
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] no crtc found\n", m_id);
|
||||
drmModeFreeEncoder(p_encoder);
|
||||
}
|
||||
output_position++;
|
||||
}
|
||||
drmModeFreeConnector(p_connector);
|
||||
}
|
||||
else
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] card %d connector %d - %d\n", m_id, num, i, p_res->connectors[i]);
|
||||
}
|
||||
drmModeFreeResources(p_res);
|
||||
if (!m_desktop_output)
|
||||
close(m_drm_fd);
|
||||
else
|
||||
{
|
||||
if (drmIsMaster(m_drm_fd))
|
||||
{
|
||||
s_shared_fd[m_card_id] = m_drm_fd;
|
||||
s_shared_count[m_card_id] = 1;
|
||||
drmDropMaster(m_drm_fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_shared_count[m_card_id] > 0)
|
||||
{
|
||||
close(m_drm_fd);
|
||||
m_drm_fd = s_shared_fd[m_card_id];
|
||||
s_shared_count[m_card_id]++;
|
||||
}
|
||||
else if (m_id == 1)
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (init) looking for the DRM master\n", m_id);
|
||||
int fd = drm_master_hook(m_drm_fd);
|
||||
if (fd)
|
||||
{
|
||||
close(m_drm_fd);
|
||||
m_drm_fd = fd;
|
||||
s_shared_fd[m_card_id] = m_drm_fd;
|
||||
// start at 2 to disable closing the fd
|
||||
s_shared_count[m_card_id] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!drmIsMaster(m_drm_fd))
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] limited DRM rights on this screen\n", m_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!num)
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] cannot open device %s\n", m_id, drm_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle no screen detected case
|
||||
if (!m_desktop_output)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (init) [ERROR] no screen detected\n", m_id);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::drm_master_hook
|
||||
//============================================================
|
||||
|
||||
int drmkms_timing::drm_master_hook(int last_fd)
|
||||
{
|
||||
for (int fd = 4; fd < last_fd; fd++)
|
||||
{
|
||||
struct stat st;
|
||||
if (!fstat(fd, &st))
|
||||
{
|
||||
// in case of multiple video cards, it wouldd be better to compare dri number
|
||||
if (S_ISCHR(st.st_mode))
|
||||
{
|
||||
if (drmIsMaster(fd))
|
||||
{
|
||||
drmVersion *version_hook = drmGetVersion(m_drm_fd);
|
||||
log_verbose("DRM/KMS: <%d> (init) DRM hook created version %d.%d.%d type %s\n", m_id, version_hook->version_major, version_hook->version_minor, version_hook->version_patchlevel, version_hook->name);
|
||||
drmFreeVersion(version_hook);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::update_mode
|
||||
//============================================================
|
||||
|
||||
bool drmkms_timing::update_mode(modeline *mode)
|
||||
{
|
||||
if (!mode)
|
||||
return false;
|
||||
|
||||
if (!m_desktop_output)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (update_mode) [ERROR] no screen detected\n", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!delete_mode(mode))
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (update_mode) [ERROR] delete operation not successful", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!add_mode(mode))
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (update_mode) [ERROR] add operation not successful", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::add_mode
|
||||
//============================================================
|
||||
|
||||
bool drmkms_timing::add_mode(modeline *mode)
|
||||
{
|
||||
if (!mode)
|
||||
return false;
|
||||
|
||||
// Handle no screen detected case
|
||||
if (!m_desktop_output)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (add_mode) [ERROR] no screen detected\n", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mp_crtc_desktop)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (add_mode) [ERROR] no desktop crtc\n", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mode)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::set_timing
|
||||
//============================================================
|
||||
|
||||
bool drmkms_timing::set_timing(modeline *mode)
|
||||
{
|
||||
if (!mode)
|
||||
return false;
|
||||
|
||||
// Handle no screen detected case
|
||||
if (!m_desktop_output)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (set_timing) [ERROR] no screen detected\n", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
drmSetMaster(m_drm_fd);
|
||||
|
||||
// Setup the DRM mode structure
|
||||
drmModeModeInfo dmode = {};
|
||||
|
||||
// Create specific mode name
|
||||
snprintf(dmode.name, 32, "SR-%d_%dx%d@%.02f%s", m_id, mode->hactive, mode->vactive, mode->vfreq, mode->interlace ? "i" : "");
|
||||
dmode.clock = mode->pclock / 1000;
|
||||
dmode.hdisplay = mode->hactive;
|
||||
dmode.hsync_start = mode->hbegin;
|
||||
dmode.hsync_end = mode->hend;
|
||||
dmode.htotal = mode->htotal;
|
||||
dmode.vdisplay = mode->vactive;
|
||||
dmode.vsync_start = mode->vbegin;
|
||||
dmode.vsync_end = mode->vend;
|
||||
dmode.vtotal = mode->vtotal;
|
||||
dmode.flags = (mode->interlace ? DRM_MODE_FLAG_INTERLACE : 0) | (mode->doublescan ? DRM_MODE_FLAG_DBLSCAN : 0) | (mode->hsync ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | (mode->vsync ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC);
|
||||
|
||||
dmode.hskew = 0;
|
||||
dmode.vscan = 0;
|
||||
|
||||
dmode.vrefresh = mode->refresh; // Used only for human readable output
|
||||
|
||||
dmode.type = DRM_MODE_TYPE_USERDEF; //DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_DRMKMS;
|
||||
|
||||
if (mode->platform_data == 4815162342)
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (set_timing) <debug> restore desktop mode\n", m_id);
|
||||
drmModeSetCrtc(m_drm_fd, mp_crtc_desktop->crtc_id, mp_crtc_desktop->buffer_id, mp_crtc_desktop->x, mp_crtc_desktop->y, &m_desktop_output, 1, &mp_crtc_desktop->mode);
|
||||
if (m_dumb_handle)
|
||||
{
|
||||
int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &m_dumb_handle);
|
||||
if (ret)
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_DESTROY_DUMB %d\n", m_id, ret);
|
||||
m_dumb_handle = 0;
|
||||
}
|
||||
if (m_framebuffer_id && m_framebuffer_id != mp_crtc_desktop->buffer_id)
|
||||
{
|
||||
if (drmModeRmFB(m_drm_fd, m_framebuffer_id))
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] remove frame buffer\n", m_id);
|
||||
m_framebuffer_id = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int old_dumb_handle = m_dumb_handle;
|
||||
|
||||
drmModeFB *pframebuffer = drmModeGetFB(m_drm_fd, mp_crtc_desktop->buffer_id);
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) <debug> existing frame buffer id %d size %dx%d bpp %d\n", m_id, mp_crtc_desktop->buffer_id, pframebuffer->width, pframebuffer->height, pframebuffer->bpp);
|
||||
//drmModePlaneRes *pplanes = drmModeGetPlaneResources(m_drm_fd);
|
||||
//log_verbose("DRM/KMS: <%d> (add_mode) <debug> total planes %d\n", m_id, pplanes->count_planes);
|
||||
//drmModeFreePlaneResources(pplanes);
|
||||
|
||||
unsigned int framebuffer_id = mp_crtc_desktop->buffer_id;
|
||||
|
||||
//if (pframebuffer->width < dmode.hdisplay || pframebuffer->height < dmode.vdisplay)
|
||||
if (1)
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) <debug> creating new frame buffer with size %dx%d\n", m_id, dmode.hdisplay, dmode.vdisplay);
|
||||
|
||||
// create a new dumb fb (not driver specefic)
|
||||
drm_mode_create_dumb create_dumb = {};
|
||||
create_dumb.width = dmode.hdisplay;
|
||||
create_dumb.height = dmode.vdisplay;
|
||||
create_dumb.bpp = pframebuffer->bpp;
|
||||
|
||||
int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
|
||||
if (ret)
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_CREATE_DUMB %d\n", m_id, ret);
|
||||
|
||||
if (drmModeAddFB(m_drm_fd, dmode.hdisplay, dmode.vdisplay, pframebuffer->depth, pframebuffer->bpp, create_dumb.pitch, create_dumb.handle, &framebuffer_id))
|
||||
log_error("DRM/KMS: <%d> (add_mode) [ERROR] cannot add frame buffer\n", m_id);
|
||||
else
|
||||
m_dumb_handle = create_dumb.handle;
|
||||
|
||||
drm_mode_map_dumb map_dumb = {};
|
||||
map_dumb.handle = create_dumb.handle;
|
||||
|
||||
ret = drmIoctl(m_drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
|
||||
if (ret)
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_MAP_DUMB %d\n", m_id, ret);
|
||||
|
||||
void *map = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, m_drm_fd, map_dumb.offset);
|
||||
if (map != MAP_FAILED)
|
||||
{
|
||||
// clear the frame buffer
|
||||
memset(map, 0, create_dumb.size);
|
||||
}
|
||||
else
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] failed to map frame buffer %p\n", m_id, map);
|
||||
}
|
||||
else
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) <debug> use existing frame buffer\n", m_id);
|
||||
|
||||
drmModeFreeFB(pframebuffer);
|
||||
|
||||
pframebuffer = drmModeGetFB(m_drm_fd, framebuffer_id);
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) <debug> frame buffer id %d size %dx%d bpp %d\n", m_id, framebuffer_id, pframebuffer->width, pframebuffer->height, pframebuffer->bpp);
|
||||
drmModeFreeFB(pframebuffer);
|
||||
|
||||
// set the mode on the crtc
|
||||
if (drmModeSetCrtc(m_drm_fd, mp_crtc_desktop->crtc_id, framebuffer_id, 0, 0, &m_desktop_output, 1, &dmode))
|
||||
log_error("DRM/KMS: <%d> (add_mode) [ERROR] cannot attach the mode to the crtc %d frame buffer %d\n", m_id, mp_crtc_desktop->crtc_id, framebuffer_id);
|
||||
else
|
||||
{
|
||||
if (old_dumb_handle)
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) <debug> remove old dumb %d\n", m_id, old_dumb_handle);
|
||||
int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &old_dumb_handle);
|
||||
if (ret)
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_DESTROY_DUMB %d\n", m_id, ret);
|
||||
old_dumb_handle = 0;
|
||||
}
|
||||
if (m_framebuffer_id && framebuffer_id != mp_crtc_desktop->buffer_id)
|
||||
{
|
||||
log_verbose("DRM/KMS: <%d> (add_mode) <debug> remove old frame buffer %d\n", m_id, m_framebuffer_id);
|
||||
drmModeRmFB(m_drm_fd, m_framebuffer_id);
|
||||
m_framebuffer_id = 0;
|
||||
}
|
||||
m_framebuffer_id = framebuffer_id;
|
||||
}
|
||||
}
|
||||
drmDropMaster(m_drm_fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::delete_mode
|
||||
//============================================================
|
||||
|
||||
bool drmkms_timing::delete_mode(modeline *mode)
|
||||
{
|
||||
if (!mode)
|
||||
return false;
|
||||
|
||||
// Handle no screen detected case
|
||||
if (!m_desktop_output)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (delete_mode) [ERROR] no screen detected\n", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// drmkms_timing::get_timing
|
||||
//============================================================
|
||||
|
||||
bool drmkms_timing::get_timing(modeline *mode)
|
||||
{
|
||||
// Handle no screen detected case
|
||||
if (!m_desktop_output)
|
||||
{
|
||||
log_error("DRM/KMS: <%d> (get_timing) [ERROR] no screen detected\n", m_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// INFO: not used vrefresh, hskew, vscan
|
||||
drmModeRes *p_res = drmModeGetResources(m_drm_fd);
|
||||
|
||||
for (int i = 0; i < p_res->count_connectors; i++)
|
||||
{
|
||||
drmModeConnector *p_connector = drmModeGetConnector(m_drm_fd, p_res->connectors[i]);
|
||||
|
||||
// Cycle through the modelines and report them back to the display manager
|
||||
if (p_connector && m_desktop_output == p_connector->connector_id)
|
||||
{
|
||||
if (m_video_modes_position < p_connector->count_modes)
|
||||
{
|
||||
drmModeModeInfo *pdmode = &p_connector->modes[m_video_modes_position++];
|
||||
|
||||
// Use mode position as index
|
||||
mode->platform_data = m_video_modes_position;
|
||||
|
||||
mode->pclock = pdmode->clock * 1000;
|
||||
mode->hactive = pdmode->hdisplay;
|
||||
mode->hbegin = pdmode->hsync_start;
|
||||
mode->hend = pdmode->hsync_end;
|
||||
mode->htotal = pdmode->htotal;
|
||||
mode->vactive = pdmode->vdisplay;
|
||||
mode->vbegin = pdmode->vsync_start;
|
||||
mode->vend = pdmode->vsync_end;
|
||||
mode->vtotal = pdmode->vtotal;
|
||||
mode->interlace = (pdmode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
|
||||
mode->doublescan = (pdmode->flags & DRM_MODE_FLAG_DBLSCAN) ? 1 : 0;
|
||||
mode->hsync = (pdmode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
|
||||
mode->vsync = (pdmode->flags & DRM_MODE_FLAG_PVSYNC) ? 1 : 0;
|
||||
|
||||
mode->hfreq = mode->pclock / mode->htotal;
|
||||
mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace ? 2 : 1);
|
||||
mode->refresh = mode->vfreq;
|
||||
|
||||
mode->width = pdmode->hdisplay;
|
||||
mode->height = pdmode->vdisplay;
|
||||
|
||||
// Add the rotation flag from the plane (DRM_MODE_ROTATE_xxx)
|
||||
// TODO: mode->type |= MODE_ROTATED;
|
||||
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_DRMKMS;
|
||||
|
||||
if (strncmp(pdmode->name, "SR-", 3) == 0)
|
||||
log_verbose("DRM/KMS: <%d> (get_timing) [WARNING] modeline %s detected\n", m_id, pdmode->name);
|
||||
else if (!strcmp(pdmode->name, mp_crtc_desktop->mode.name) && pdmode->clock == mp_crtc_desktop->mode.clock && pdmode->vrefresh == mp_crtc_desktop->mode.vrefresh)
|
||||
{
|
||||
// Add the desktop flag to desktop modeline
|
||||
log_verbose("DRM/KMS: <%d> (get_timing) desktop mode name %s refresh %d found\n", m_id, mp_crtc_desktop->mode.name, mp_crtc_desktop->mode.vrefresh);
|
||||
mode->type |= MODE_DESKTOP;
|
||||
mode->platform_data = 4815162342;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inititalise the position for the modeline list
|
||||
m_video_modes_position = 0;
|
||||
}
|
||||
}
|
||||
drmModeFreeConnector(p_connector);
|
||||
}
|
||||
drmModeFreeResources(p_res);
|
||||
|
||||
return true;
|
||||
}
|
||||
Vendored
+81
@@ -0,0 +1,81 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_drmkms.h - Linux DRM/KMS video management layer
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#ifndef __CUSTOM_VIDEO_DRMKMS_
|
||||
#define __CUSTOM_VIDEO_DRMKMS_
|
||||
|
||||
// DRM headers
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include "custom_video.h"
|
||||
|
||||
class drmkms_timing : public custom_video
|
||||
{
|
||||
public:
|
||||
drmkms_timing(char *device_name, custom_video_settings *vs);
|
||||
~drmkms_timing();
|
||||
const char *api_name() { return "DRMKMS"; }
|
||||
int caps() { return CUSTOM_VIDEO_CAPS_ADD; }
|
||||
bool init();
|
||||
|
||||
bool add_mode(modeline *mode);
|
||||
bool delete_mode(modeline *mode);
|
||||
bool update_mode(modeline *mode);
|
||||
|
||||
bool get_timing(modeline *mode);
|
||||
bool set_timing(modeline *mode);
|
||||
|
||||
private:
|
||||
int m_id = 0;
|
||||
|
||||
int m_drm_fd = 0;
|
||||
drmModeCrtc *mp_crtc_desktop = NULL;
|
||||
int m_card_id = 0;
|
||||
int drm_master_hook(int fd);
|
||||
|
||||
char m_device_name[32];
|
||||
unsigned int m_desktop_output = 0;
|
||||
int m_video_modes_position = 0;
|
||||
|
||||
void *mp_drm_handle = NULL;
|
||||
unsigned int m_dumb_handle = 0;
|
||||
unsigned int m_framebuffer_id = 0;
|
||||
|
||||
__typeof__(drmGetVersion) *p_drmGetVersion;
|
||||
__typeof__(drmFreeVersion) *p_drmFreeVersion;
|
||||
__typeof__(drmModeGetResources) *p_drmModeGetResources;
|
||||
__typeof__(drmModeGetConnector) *p_drmModeGetConnector;
|
||||
__typeof__(drmModeFreeConnector) *p_drmModeFreeConnector;
|
||||
__typeof__(drmModeFreeResources) *p_drmModeFreeResources;
|
||||
__typeof__(drmModeGetEncoder) *p_drmModeGetEncoder;
|
||||
__typeof__(drmModeFreeEncoder) *p_drmModeFreeEncoder;
|
||||
__typeof__(drmModeGetCrtc) *p_drmModeGetCrtc;
|
||||
__typeof__(drmModeSetCrtc) *p_drmModeSetCrtc;
|
||||
__typeof__(drmModeFreeCrtc) *p_drmModeFreeCrtc;
|
||||
__typeof__(drmModeAttachMode) *p_drmModeAttachMode;
|
||||
__typeof__(drmModeAddFB) *p_drmModeAddFB;
|
||||
__typeof__(drmModeRmFB) *p_drmModeRmFB;
|
||||
__typeof__(drmModeGetFB) *p_drmModeGetFB;
|
||||
__typeof__(drmModeFreeFB) *p_drmModeFreeFB;
|
||||
__typeof__(drmPrimeHandleToFD) *p_drmPrimeHandleToFD;
|
||||
__typeof__(drmModeGetPlaneResources) *p_drmModeGetPlaneResources;
|
||||
__typeof__(drmModeFreePlaneResources) *p_drmModeFreePlaneResources;
|
||||
__typeof__(drmIoctl) *p_drmIoctl;
|
||||
__typeof__(drmGetCap) *p_drmGetCap;
|
||||
__typeof__(drmIsMaster) *p_drmIsMaster;
|
||||
__typeof__(drmSetMaster) *p_drmSetMaster;
|
||||
__typeof__(drmDropMaster) *p_drmDropMaster;
|
||||
};
|
||||
|
||||
#endif
|
||||
+628
@@ -0,0 +1,628 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_pstrip.cpp - PowerStrip interface routines
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
/* http://forums.entechtaiwan.com/index.php?topic=5534.msg20902;topicseen#msg20902
|
||||
|
||||
UM_SETCUSTOMTIMING = WM_USER+200;
|
||||
wparam = monitor number, zero-based
|
||||
lparam = atom for string pointer
|
||||
lresult = -1 for failure else current pixel clock (integer in Hz)
|
||||
Note: pass full PowerStrip timing string*
|
||||
|
||||
UM_SETREFRESHRATE = WM_USER+201;
|
||||
wparam = monitor number, zero-based
|
||||
lparam = refresh rate (integer in Hz), or 0 for read-only
|
||||
lresult = -1 for failure else current refresh rate (integer in Hz)
|
||||
|
||||
UM_SETPOLARITY = WM_USER+202;
|
||||
wparam = monitor number, zero-based
|
||||
lparam = polarity bits
|
||||
lresult = -1 for failure else current polarity bits+1
|
||||
|
||||
UM_REMOTECONTROL = WM_USER+210;
|
||||
wparam = 99
|
||||
lparam =
|
||||
0 to hide tray icon
|
||||
1 to show tray icon,
|
||||
2 to get build number
|
||||
10 to show Performance profiles
|
||||
11 to show Color profiles
|
||||
12 to show Display profiles
|
||||
13 to show Application profiles
|
||||
14 to show Adapter information
|
||||
15 to show Monitor information
|
||||
16 to show Hotkey manager
|
||||
17 to show Resource manager
|
||||
18 to show Preferences
|
||||
19 to show Online services
|
||||
20 to show About screen
|
||||
21 to show Tip-of-the-day
|
||||
22 to show Setup wizard
|
||||
23 to show Screen fonts
|
||||
24 to show Advanced timing options
|
||||
25 to show Custom resolutions
|
||||
99 to close PS
|
||||
lresult = -1 for failure else lparam+1 for success or build number (e.g., 335)
|
||||
if lparam was 2
|
||||
|
||||
UM_SETGAMMARAMP = WM_USER+203;
|
||||
wparam = monitor number, zero-based
|
||||
lparam = atom for string pointer
|
||||
lresult = -1 for failure, 1 for success
|
||||
|
||||
UM_CREATERESOLUTION = WM_USER+204;
|
||||
wparam = monitor number, zero-based
|
||||
lparam = atom for string pointer
|
||||
lresult = -1 for failure, 1 for success
|
||||
Note: pass full PowerStrip timing string*; reboot is usually necessary to see if
|
||||
the resolution is accepted by the display driver
|
||||
|
||||
UM_GETTIMING = WM_USER+205;
|
||||
wparam = monitor number, zero-based
|
||||
lresult = -1 for failure else GlobalAtom number identifiying the timing string*
|
||||
Note: be sure to call GlobalDeleteAtom after reading the string associated with
|
||||
the atom
|
||||
|
||||
UM_GETSETCLOCKS = WM_USER+206;
|
||||
wparam = monitor number, zero-based
|
||||
lparam = atom for string pointer
|
||||
lresult = -1 for failure else GlobalAtom number identifiying the performance
|
||||
string**
|
||||
Note: pass full PowerStrip performance string** to set the clocks, and ull to
|
||||
get clocks; be sure to call GlobalDeleteAtom after reading the string associated
|
||||
with the atom
|
||||
|
||||
NegativeHorizontalPolarity = 0x02;
|
||||
NegativeVerticalPolarity = 0x04;
|
||||
|
||||
*Timing string parameter definition:
|
||||
1 = horizontal active pixels
|
||||
2 = horizontal front porch
|
||||
3 = horizontal sync width
|
||||
4 = horizontal back porch
|
||||
5 = vertical active pixels
|
||||
6 = vertical front porch
|
||||
7 = vertical sync width
|
||||
8 = vertical back porch
|
||||
9 = pixel clock in hertz
|
||||
10 = timing flags, where bit:
|
||||
1 = negative horizontal porlarity
|
||||
2 = negative vertical polarity
|
||||
3 = interlaced
|
||||
5 = composite sync
|
||||
7 = sync-on-green
|
||||
all other bits reserved
|
||||
|
||||
**Performance string parameter definition:
|
||||
1 = memory clock in hertz
|
||||
2 = engine clock in hertz
|
||||
3 = reserved
|
||||
4 = reserved
|
||||
5 = reserved
|
||||
6 = reserved
|
||||
7 = reserved
|
||||
8 = reserved
|
||||
9 = 2D memory clock in hertz (if different from 3D)
|
||||
10 = 2D engine clock in hertz (if different from 3D) */
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "custom_video_pstrip.h"
|
||||
#include "log.h"
|
||||
|
||||
//============================================================
|
||||
// CONSTANTS
|
||||
//============================================================
|
||||
|
||||
#define UM_SETCUSTOMTIMING (WM_USER+200)
|
||||
#define UM_SETREFRESHRATE (WM_USER+201)
|
||||
#define UM_SETPOLARITY (WM_USER+202)
|
||||
#define UM_REMOTECONTROL (WM_USER+210)
|
||||
#define UM_SETGAMMARAMP (WM_USER+203)
|
||||
#define UM_CREATERESOLUTION (WM_USER+204)
|
||||
#define UM_GETTIMING (WM_USER+205)
|
||||
#define UM_GETSETCLOCKS (WM_USER+206)
|
||||
#define UM_SETCUSTOMTIMINGFAST (WM_USER+211) // glitches vertical sync with PS 3.65 build 568
|
||||
|
||||
#define NegativeHorizontalPolarity 0x02
|
||||
#define NegativeVerticalPolarity 0x04
|
||||
#define Interlace 0x08
|
||||
|
||||
#define HideTrayIcon 0x00
|
||||
#define ShowTrayIcon 0x01
|
||||
#define ClosePowerStrip 0x63
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::pstrip_timing
|
||||
//============================================================
|
||||
|
||||
pstrip_timing::pstrip_timing(char *device_name, custom_video_settings *vs)
|
||||
{
|
||||
m_vs = *vs;
|
||||
strcpy (m_device_name, device_name);
|
||||
strcpy (m_ps_timing, m_vs.custom_timing);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::~pstrip_timing()
|
||||
//============================================================
|
||||
|
||||
pstrip_timing::~pstrip_timing()
|
||||
{
|
||||
ps_reset();
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::init
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::init()
|
||||
{
|
||||
m_monitor_index = ps_monitor_index(m_device_name);
|
||||
|
||||
hPSWnd = FindWindowA("TPShidden", NULL);
|
||||
|
||||
if (hPSWnd)
|
||||
{
|
||||
log_verbose("PStrip: PowerStrip found!\n");
|
||||
|
||||
// Save current settings
|
||||
ps_get_monitor_timing(&m_timing_backup);
|
||||
|
||||
// If we have a -ps_timing string defined, use it as user defined modeline
|
||||
if (strcmp(m_ps_timing, "auto"))
|
||||
{
|
||||
MonitorTiming timing;
|
||||
if (ps_read_timing_string(m_ps_timing, &timing))
|
||||
{
|
||||
ps_pstiming_to_modeline(&timing, &m_user_mode);
|
||||
m_user_mode.type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
|
||||
|
||||
char modeline_txt[256]={'\x00'};
|
||||
log_verbose("SwitchRes: ps_string: %s (%s)\n", m_ps_timing, modeline_print(&m_user_mode, modeline_txt, MS_PARAMS));
|
||||
}
|
||||
else
|
||||
log_verbose("Switchres: ps_timing string with invalid format\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("PStrip: Could not get PowerStrip API interface\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::get_timing
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::get_timing(modeline *mode)
|
||||
{
|
||||
// If we have an user defined mode (ps_timing), lock any non matching mode
|
||||
if (m_user_mode.hactive)
|
||||
{
|
||||
if (mode->width != m_user_mode.width || mode->height != m_user_mode.height)
|
||||
{
|
||||
mode->type |= MODE_DISABLED;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
modeline m_temp = {};
|
||||
if (ps_get_modeline(&m_temp))
|
||||
{
|
||||
// We can only get the timings of the current desktop mode, so filter out anything different
|
||||
if (m_temp.width == mode->width && m_temp.height == mode->height && m_temp.refresh == mode->refresh)
|
||||
{
|
||||
*mode = m_temp;
|
||||
}
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::set_timing
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::set_timing(modeline *mode)
|
||||
{
|
||||
// In case -ps_timing is provided, pass it as raw string
|
||||
if (m_user_mode.hactive)
|
||||
ps_set_monitor_timing_string(m_ps_timing);
|
||||
|
||||
// Otherwise pass it as modeline
|
||||
else
|
||||
ps_set_modeline(mode);
|
||||
|
||||
Sleep(100);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_reset
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_reset()
|
||||
{
|
||||
return ps_set_monitor_timing(&m_timing_backup);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// ps_get_modeline
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_get_modeline(modeline *modeline)
|
||||
{
|
||||
MonitorTiming timing = {};
|
||||
|
||||
if (ps_get_monitor_timing(&timing))
|
||||
{
|
||||
ps_pstiming_to_modeline(&timing, modeline);
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_set_modeline
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::ps_set_modeline(modeline *modeline)
|
||||
{
|
||||
MonitorTiming timing = {};
|
||||
|
||||
if (!ps_modeline_to_pstiming(modeline, &timing))
|
||||
return false;
|
||||
|
||||
timing.PixelClockInKiloHertz = ps_best_pclock(&timing, timing.PixelClockInKiloHertz);
|
||||
|
||||
if (ps_set_monitor_timing(&timing))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_get_monitor_timing
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_get_monitor_timing(MonitorTiming *timing)
|
||||
{
|
||||
LRESULT lresult;
|
||||
char in[256];
|
||||
|
||||
if (!hPSWnd) return 0;
|
||||
|
||||
lresult = SendMessage(hPSWnd, UM_GETTIMING, m_monitor_index, 0);
|
||||
|
||||
if (lresult == -1)
|
||||
{
|
||||
log_verbose("PStrip: Could not get PowerStrip timing string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!GlobalGetAtomNameA(lresult, in, sizeof(in)))
|
||||
{
|
||||
log_verbose("PStrip: GlobalGetAtomName failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_verbose("PStrip: ps_get_monitor_timing(%d): %s\n", m_monitor_index, in);
|
||||
|
||||
ps_read_timing_string(in, timing);
|
||||
|
||||
GlobalDeleteAtom(lresult); // delete atom created by PowerStrip
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_set_monitor_timing
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_set_monitor_timing(MonitorTiming *timing)
|
||||
{
|
||||
LRESULT lresult;
|
||||
ATOM atom;
|
||||
char out[256];
|
||||
|
||||
if (!hPSWnd) return 0;
|
||||
|
||||
ps_fill_timing_string(out, timing);
|
||||
atom = GlobalAddAtomA(out);
|
||||
|
||||
if (atom)
|
||||
{
|
||||
lresult = SendMessage(hPSWnd, UM_SETCUSTOMTIMING, m_monitor_index, atom);
|
||||
|
||||
if (lresult < 0)
|
||||
{
|
||||
log_verbose("PStrip: SendMessage failed\n");
|
||||
GlobalDeleteAtom(atom);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("PStrip: ps_set_monitor_timing(%d): %s\n", m_monitor_index, out);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else log_verbose("PStrip: ps_set_monitor_timing atom creation failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_set_monitor_timing_string
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_set_monitor_timing_string(char *in)
|
||||
{
|
||||
MonitorTiming timing = {};
|
||||
|
||||
ps_read_timing_string(in, &timing);
|
||||
return ps_set_monitor_timing(&timing);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_set_refresh
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_set_refresh(double vfreq)
|
||||
{
|
||||
MonitorTiming timing = {};
|
||||
int hht, vvt, new_vvt;
|
||||
int desired_pClock;
|
||||
int best_pClock;
|
||||
|
||||
memcpy(&timing, &m_timing_backup, sizeof(MonitorTiming));
|
||||
|
||||
hht = timing.HorizontalActivePixels
|
||||
+ timing.HorizontalFrontPorch
|
||||
+ timing.HorizontalSyncWidth
|
||||
+ timing.HorizontalBackPorch;
|
||||
|
||||
vvt = timing.VerticalActivePixels
|
||||
+ timing.VerticalFrontPorch
|
||||
+ timing.VerticalSyncWidth
|
||||
+ timing.VerticalBackPorch;
|
||||
|
||||
desired_pClock = hht * vvt * vfreq / 1000;
|
||||
best_pClock = ps_best_pclock(&timing, desired_pClock);
|
||||
|
||||
new_vvt = best_pClock * 1000 / (vfreq * hht);
|
||||
|
||||
timing.VerticalBackPorch += (new_vvt - vvt);
|
||||
timing.PixelClockInKiloHertz = best_pClock;
|
||||
|
||||
ps_set_monitor_timing(&timing);
|
||||
ps_get_monitor_timing(&timing);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_best_pclock
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_best_pclock(MonitorTiming *timing, int desired_pclock)
|
||||
{
|
||||
MonitorTiming timing_read = {};
|
||||
int best_pclock = 0;
|
||||
|
||||
log_verbose("PStrip: ps_best_pclock(%d), getting stable dotclocks for %d...\n", m_monitor_index, desired_pclock);
|
||||
|
||||
for (int i = -50; i <= 50; i += 25)
|
||||
{
|
||||
timing->PixelClockInKiloHertz = desired_pclock + i;
|
||||
|
||||
ps_set_monitor_timing(timing);
|
||||
ps_get_monitor_timing(&timing_read);
|
||||
|
||||
if (abs(timing_read.PixelClockInKiloHertz - desired_pclock) < abs(desired_pclock - best_pclock))
|
||||
best_pclock = timing_read.PixelClockInKiloHertz;
|
||||
}
|
||||
|
||||
log_verbose("PStrip: ps_best_pclock(%d), new dotclock: %d\n", m_monitor_index, best_pclock);
|
||||
|
||||
return best_pclock;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_create_resolution
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_create_resolution(modeline *modeline)
|
||||
{
|
||||
LRESULT lresult;
|
||||
ATOM atom;
|
||||
char out[256];
|
||||
MonitorTiming timing = {};
|
||||
|
||||
if (!hPSWnd) return 0;
|
||||
|
||||
ps_modeline_to_pstiming(modeline, &timing);
|
||||
|
||||
ps_fill_timing_string(out, &timing);
|
||||
atom = GlobalAddAtomA(out);
|
||||
|
||||
if (atom)
|
||||
{
|
||||
lresult = SendMessage(hPSWnd, UM_CREATERESOLUTION, m_monitor_index, atom);
|
||||
|
||||
if (lresult < 0)
|
||||
{
|
||||
log_verbose("PStrip: SendMessage failed\n");
|
||||
GlobalDeleteAtom(atom);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("PStrip: ps_create_resolution(%d): %dx%d succeded \n",
|
||||
modeline->width, modeline->height, m_monitor_index);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else log_verbose("PStrip: ps_create_resolution atom creation failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_read_timing_string
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::ps_read_timing_string(char *in, MonitorTiming *timing)
|
||||
{
|
||||
if (sscanf(in,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
&timing->HorizontalActivePixels,
|
||||
&timing->HorizontalFrontPorch,
|
||||
&timing->HorizontalSyncWidth,
|
||||
&timing->HorizontalBackPorch,
|
||||
&timing->VerticalActivePixels,
|
||||
&timing->VerticalFrontPorch,
|
||||
&timing->VerticalSyncWidth,
|
||||
&timing->VerticalBackPorch,
|
||||
&timing->PixelClockInKiloHertz,
|
||||
&timing->TimingFlags.w) == 10) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_fill_timing_string
|
||||
//============================================================
|
||||
|
||||
void pstrip_timing::ps_fill_timing_string(char *out, MonitorTiming *timing)
|
||||
{
|
||||
sprintf(out, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
timing->HorizontalActivePixels,
|
||||
timing->HorizontalFrontPorch,
|
||||
timing->HorizontalSyncWidth,
|
||||
timing->HorizontalBackPorch,
|
||||
timing->VerticalActivePixels,
|
||||
timing->VerticalFrontPorch,
|
||||
timing->VerticalSyncWidth,
|
||||
timing->VerticalBackPorch,
|
||||
timing->PixelClockInKiloHertz,
|
||||
timing->TimingFlags.w);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_modeline_to_pstiming
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing)
|
||||
{
|
||||
if (modeline->pclock == 0 || modeline->hactive == 0 || modeline->vactive == 0)
|
||||
{
|
||||
log_verbose("ps_modeline_to_pstiming error: invalid modeline\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
timing->HorizontalActivePixels = modeline->hactive;
|
||||
timing->HorizontalFrontPorch = modeline->hbegin - modeline->hactive;
|
||||
timing->HorizontalSyncWidth = modeline->hend - modeline->hbegin;
|
||||
timing->HorizontalBackPorch = modeline->htotal - modeline->hend;
|
||||
|
||||
timing->VerticalActivePixels = modeline->vactive;
|
||||
timing->VerticalFrontPorch = modeline->vbegin - modeline->vactive;
|
||||
timing->VerticalSyncWidth = modeline->vend - modeline->vbegin;
|
||||
timing->VerticalBackPorch = modeline->vtotal - modeline->vend;
|
||||
|
||||
timing->PixelClockInKiloHertz = modeline->pclock / 1000;
|
||||
|
||||
if (modeline->hsync == 0)
|
||||
timing->TimingFlags.w |= NegativeHorizontalPolarity;
|
||||
if (modeline->vsync == 0)
|
||||
timing->TimingFlags.w |= NegativeVerticalPolarity;
|
||||
if (modeline->interlace)
|
||||
timing->TimingFlags.w |= Interlace;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_pstiming_to_modeline
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline)
|
||||
{
|
||||
modeline->hactive = timing->HorizontalActivePixels;
|
||||
modeline->hbegin = modeline->hactive + timing->HorizontalFrontPorch;
|
||||
modeline->hend = modeline->hbegin + timing->HorizontalSyncWidth;
|
||||
modeline->htotal = modeline->hend + timing->HorizontalBackPorch;
|
||||
|
||||
modeline->vactive = timing->VerticalActivePixels;
|
||||
modeline->vbegin = modeline->vactive + timing->VerticalFrontPorch;
|
||||
modeline->vend = modeline->vbegin + timing->VerticalSyncWidth;
|
||||
modeline->vtotal = modeline->vend + timing->VerticalBackPorch;
|
||||
|
||||
modeline->width = modeline->hactive;
|
||||
modeline->height = modeline->vactive;
|
||||
|
||||
modeline->pclock = timing->PixelClockInKiloHertz * 1000;
|
||||
|
||||
if (!(timing->TimingFlags.w & NegativeHorizontalPolarity))
|
||||
modeline->hsync = 1;
|
||||
|
||||
if (!(timing->TimingFlags.w & NegativeVerticalPolarity))
|
||||
modeline->vsync = 1;
|
||||
|
||||
if ((timing->TimingFlags.w & Interlace))
|
||||
modeline->interlace = 1;
|
||||
|
||||
modeline->hfreq = modeline->pclock / modeline->htotal;
|
||||
modeline->vfreq = modeline->hfreq / modeline->vtotal * (modeline->interlace?2:1);
|
||||
modeline->refresh = int(modeline->vfreq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::ps_monitor_index
|
||||
//============================================================
|
||||
|
||||
int pstrip_timing::ps_monitor_index (const char *display_name)
|
||||
{
|
||||
int monitor_index = 0;
|
||||
char sub_index[2];
|
||||
|
||||
sub_index[0] = display_name[strlen(display_name)-1];
|
||||
sub_index[1] = 0;
|
||||
if (sscanf(sub_index,"%d", &monitor_index) == 1)
|
||||
monitor_index --;
|
||||
|
||||
return monitor_index;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// pstrip_timing::update_mode
|
||||
//============================================================
|
||||
|
||||
bool pstrip_timing::update_mode(modeline *mode)
|
||||
{
|
||||
if (!set_timing(mode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
|
||||
return true;
|
||||
}
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_powerstrip.h - PowerStrip interface routines
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include "custom_video.h"
|
||||
|
||||
//============================================================
|
||||
// TYPE DEFINITIONS
|
||||
//============================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int HorizontalActivePixels;
|
||||
int HorizontalFrontPorch;
|
||||
int HorizontalSyncWidth;
|
||||
int HorizontalBackPorch;
|
||||
int VerticalActivePixels;
|
||||
int VerticalFrontPorch;
|
||||
int VerticalSyncWidth;
|
||||
int VerticalBackPorch;
|
||||
int PixelClockInKiloHertz;
|
||||
union
|
||||
{
|
||||
int w;
|
||||
struct
|
||||
{
|
||||
unsigned :1;
|
||||
unsigned HorizontalPolarityNegative:1;
|
||||
unsigned VerticalPolarityNegative:1;
|
||||
unsigned :29;
|
||||
} b;
|
||||
} TimingFlags;
|
||||
} MonitorTiming;
|
||||
|
||||
|
||||
class pstrip_timing : public custom_video
|
||||
{
|
||||
public:
|
||||
pstrip_timing(char *device_name, custom_video_settings *vs);
|
||||
~pstrip_timing();
|
||||
const char *api_name() { return "PowerStrip"; }
|
||||
bool init();
|
||||
int caps() { return CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_SCAN_EDITABLE | CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE; }
|
||||
|
||||
bool update_mode(modeline *mode);
|
||||
|
||||
bool get_timing(modeline *mode);
|
||||
bool set_timing(modeline *m);
|
||||
|
||||
private:
|
||||
|
||||
int ps_reset();
|
||||
int ps_get_modeline(modeline *modeline);
|
||||
bool ps_set_modeline(modeline *modeline);
|
||||
int ps_get_monitor_timing(MonitorTiming *timing);
|
||||
int ps_set_monitor_timing(MonitorTiming *timing);
|
||||
int ps_set_monitor_timing_string(char *in);
|
||||
int ps_set_refresh(double vfreq);
|
||||
int ps_best_pclock(MonitorTiming *timing, int desired_pclock);
|
||||
int ps_create_resolution(modeline *modeline);
|
||||
bool ps_read_timing_string(char *in, MonitorTiming *timing);
|
||||
void ps_fill_timing_string(char *out, MonitorTiming *timing);
|
||||
bool ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing);
|
||||
int ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline);
|
||||
int ps_monitor_index (const char *display_name);
|
||||
|
||||
char m_device_name[32];
|
||||
char m_ps_timing[256];
|
||||
int m_monitor_index = 0;
|
||||
modeline m_user_mode = {};
|
||||
MonitorTiming m_timing_backup = {};
|
||||
HWND hPSWnd = 0;
|
||||
};
|
||||
+1190
File diff suppressed because it is too large.
Load diff
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
/**************************************************************
|
||||
|
||||
custom_video_xrandr.h - Linux XRANDR video management layer
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#ifndef __CUSTOM_VIDEO_XRANDR__
|
||||
#define __CUSTOM_VIDEO_XRANDR__
|
||||
|
||||
// X11 Xrandr headers
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include "custom_video.h"
|
||||
|
||||
// Set timing option flags
|
||||
#define XRANDR_DISABLE_CRTC_RELOCATION 0x00000001
|
||||
#define XRANDR_ENABLE_SCREEN_REORDERING 0x00000002
|
||||
|
||||
// Set timing internal flags
|
||||
#define XRANDR_SETMODE_IS_DESKTOP 0x00000001
|
||||
#define XRANDR_SETMODE_RESTORE_DESKTOP 0x00000002
|
||||
#define XRANDR_SETMODE_UPDATE_DESKTOP_CRTC 0x00000010
|
||||
#define XRANDR_SETMODE_UPDATE_OTHER_CRTC 0x00000020
|
||||
#define XRANDR_SETMODE_UPDATE_REORDERING 0x00000040
|
||||
|
||||
#define XRANDR_SETMODE_INFO_MASK 0x0000000F
|
||||
#define XRANDR_SETMODE_UPDATE_MASK 0x000000F0
|
||||
|
||||
// Super resolution placement, vertical stacking, reserved XRANDR_REORDERING_MAXIMUM_HEIGHT pixels
|
||||
//TODO confirm 1024 height is sufficient
|
||||
#define XRANDR_REORDERING_MAXIMUM_HEIGHT 1024
|
||||
|
||||
class xrandr_timing : public custom_video
|
||||
{
|
||||
public:
|
||||
xrandr_timing(char *device_name, custom_video_settings *vs);
|
||||
~xrandr_timing();
|
||||
const char *api_name() { return "XRANDR"; }
|
||||
int caps() { return CUSTOM_VIDEO_CAPS_ADD; }
|
||||
bool init();
|
||||
|
||||
bool add_mode(modeline *mode);
|
||||
bool delete_mode(modeline *mode);
|
||||
bool update_mode(modeline *mode);
|
||||
|
||||
bool get_timing(modeline *mode);
|
||||
bool set_timing(modeline *mode);
|
||||
|
||||
bool process_modelist(std::vector<modeline *>);
|
||||
|
||||
static int ms_xerrors;
|
||||
static int ms_xerrors_flag;
|
||||
|
||||
private:
|
||||
int m_id = 0;
|
||||
int m_managed = 0;
|
||||
int m_enable_screen_reordering = 0;
|
||||
int m_enable_screen_compositing = 0;
|
||||
|
||||
XRRModeInfo *find_mode(modeline *mode);
|
||||
XRRModeInfo *find_mode_by_name(char *name);
|
||||
|
||||
bool set_timing(modeline *mode, int flags);
|
||||
|
||||
int m_video_modes_position = 0;
|
||||
char m_device_name[32];
|
||||
Rotation m_desktop_rotation;
|
||||
unsigned int m_min_width;
|
||||
unsigned int m_max_width;
|
||||
unsigned int m_min_height;
|
||||
unsigned int m_max_height;
|
||||
|
||||
Display *m_pdisplay = NULL;
|
||||
Window m_root;
|
||||
int m_screen;
|
||||
|
||||
int m_desktop_output = -1;
|
||||
XRRModeInfo m_desktop_mode = {};
|
||||
int m_crtc_flags = 0;
|
||||
|
||||
XRRCrtcInfo m_last_crtc = {};
|
||||
|
||||
void *m_xrandr_handle = 0;
|
||||
|
||||
__typeof__(XRRAddOutputMode) *p_XRRAddOutputMode;
|
||||
__typeof__(XRRConfigCurrentConfiguration) *p_XRRConfigCurrentConfiguration;
|
||||
__typeof__(XRRCreateMode) *p_XRRCreateMode;
|
||||
__typeof__(XRRDeleteOutputMode) *p_XRRDeleteOutputMode;
|
||||
__typeof__(XRRDestroyMode) *p_XRRDestroyMode;
|
||||
__typeof__(XRRFreeCrtcInfo) *p_XRRFreeCrtcInfo;
|
||||
__typeof__(XRRFreeOutputInfo) *p_XRRFreeOutputInfo;
|
||||
__typeof__(XRRFreeScreenConfigInfo) *p_XRRFreeScreenConfigInfo;
|
||||
__typeof__(XRRFreeScreenResources) *p_XRRFreeScreenResources;
|
||||
__typeof__(XRRGetCrtcInfo) *p_XRRGetCrtcInfo;
|
||||
__typeof__(XRRGetOutputInfo) *p_XRRGetOutputInfo;
|
||||
__typeof__(XRRGetScreenInfo) *p_XRRGetScreenInfo;
|
||||
__typeof__(XRRGetScreenResourcesCurrent) *p_XRRGetScreenResourcesCurrent;
|
||||
__typeof__(XRRQueryVersion) *p_XRRQueryVersion;
|
||||
__typeof__(XRRSetCrtcConfig) *p_XRRSetCrtcConfig;
|
||||
__typeof__(XRRSetScreenSize) *p_XRRSetScreenSize;
|
||||
__typeof__(XRRGetScreenSizeRange) *p_XRRGetScreenSizeRange;
|
||||
|
||||
void *m_x11_handle = 0;
|
||||
|
||||
__typeof__(XCloseDisplay) *p_XCloseDisplay;
|
||||
__typeof__(XGrabServer) *p_XGrabServer;
|
||||
__typeof__(XOpenDisplay) *p_XOpenDisplay;
|
||||
__typeof__(XSync) *p_XSync;
|
||||
__typeof__(XUngrabServer) *p_XUngrabServer;
|
||||
__typeof__(XSetErrorHandler) *p_XSetErrorHandler;
|
||||
__typeof__(XClearWindow) *p_XClearWindow;
|
||||
__typeof__(XFillRectangle) *p_XFillRectangle;
|
||||
__typeof__(XCreateGC) *p_XCreateGC;
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+481
@@ -0,0 +1,481 @@
|
||||
/**************************************************************
|
||||
|
||||
display.cpp - Display manager
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "display.h"
|
||||
#if defined(_WIN32)
|
||||
#include "display_windows.h"
|
||||
#elif defined(__linux__)
|
||||
#include <string.h>
|
||||
#include "display_linux.h"
|
||||
#endif
|
||||
#include "log.h"
|
||||
|
||||
//============================================================
|
||||
// display_manager::make
|
||||
//============================================================
|
||||
|
||||
display_manager *display_manager::make(display_settings *ds)
|
||||
{
|
||||
display_manager *display = nullptr;
|
||||
|
||||
#if defined(_WIN32)
|
||||
display = new windows_display(ds);
|
||||
#elif defined(__linux__)
|
||||
display = new linux_display(ds);
|
||||
#endif
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::parse_options
|
||||
//============================================================
|
||||
|
||||
void display_manager::parse_options()
|
||||
{
|
||||
// Get user_mode as <w>x<h>@<r>
|
||||
set_user_mode(&m_ds.user_mode);
|
||||
|
||||
// Get user defined modeline (overrides user_mode)
|
||||
modeline user_mode = {};
|
||||
if (m_ds.modeline_generation)
|
||||
{
|
||||
if (modeline_parse(m_ds.user_modeline, &user_mode))
|
||||
{
|
||||
user_mode.type |= MODE_USER_DEF;
|
||||
set_user_mode(&user_mode);
|
||||
}
|
||||
}
|
||||
|
||||
// Get monitor specs
|
||||
if (user_mode.hactive)
|
||||
{
|
||||
modeline_to_monitor_range(range, &user_mode);
|
||||
monitor_show_range(range);
|
||||
}
|
||||
else
|
||||
{
|
||||
char default_monitor[] = "generic_15";
|
||||
|
||||
memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
|
||||
|
||||
if (!strcmp(m_ds.monitor, "custom"))
|
||||
for (int i = 0; i < MAX_RANGES; i++) monitor_fill_range(&range[i], m_ds.crt_range[i]);
|
||||
|
||||
else if (!strcmp(m_ds.monitor, "lcd"))
|
||||
monitor_fill_lcd_range(&range[0], m_ds.lcd_range);
|
||||
|
||||
else if (monitor_set_preset(m_ds.monitor, range) == 0)
|
||||
monitor_set_preset(default_monitor, range);
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::init
|
||||
//============================================================
|
||||
|
||||
bool display_manager::init()
|
||||
{
|
||||
sprintf(m_ds.screen, "ram");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::caps
|
||||
//============================================================
|
||||
|
||||
int display_manager::caps()
|
||||
{
|
||||
if (video())
|
||||
return video()->caps();
|
||||
else
|
||||
return CUSTOM_VIDEO_CAPS_ADD;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::add_mode
|
||||
//============================================================
|
||||
|
||||
bool display_manager::add_mode(modeline *mode)
|
||||
{
|
||||
if (video() == nullptr)
|
||||
return false;
|
||||
|
||||
// Add new mode
|
||||
if (!video()->add_mode(mode))
|
||||
{
|
||||
log_verbose("Switchres: error adding mode ");
|
||||
log_mode(mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
mode->type &= ~MODE_ADD;
|
||||
|
||||
log_verbose("Switchres: added ");
|
||||
log_mode(mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::delete_mode
|
||||
//============================================================
|
||||
|
||||
bool display_manager::delete_mode(modeline *mode)
|
||||
{
|
||||
if (video() == nullptr)
|
||||
return false;
|
||||
|
||||
if (!video()->delete_mode(mode))
|
||||
{
|
||||
log_verbose("Switchres: error deleting mode ");
|
||||
log_mode(mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_verbose("Switchres: deleted ");
|
||||
log_mode(mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::update_mode
|
||||
//============================================================
|
||||
|
||||
bool display_manager::update_mode(modeline *mode)
|
||||
{
|
||||
if (video() == nullptr)
|
||||
return false;
|
||||
|
||||
// Apply new timings
|
||||
if (!video()->update_mode(mode))
|
||||
{
|
||||
log_verbose("Switchres: error updating mode ");
|
||||
log_mode(mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
mode->type &= ~MODE_UPDATE;
|
||||
|
||||
log_verbose("Switchres: updated ");
|
||||
log_mode(mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::set_mode
|
||||
//============================================================
|
||||
|
||||
bool display_manager::set_mode(modeline *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::log_mode
|
||||
//============================================================
|
||||
|
||||
void display_manager::log_mode(modeline *mode)
|
||||
{
|
||||
char modeline_txt[256];
|
||||
log_verbose("%s timing %s\n", video()->api_name(), modeline_print(mode, modeline_txt, MS_FULL));
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::restore_modes
|
||||
//============================================================
|
||||
|
||||
bool display_manager::restore_modes()
|
||||
{
|
||||
// Compare each mode in our table with its original state
|
||||
for (unsigned i = video_modes.size(); i-- > 0; )
|
||||
{
|
||||
// First, delete all modes we've added
|
||||
if (i + 1 > backup_modes.size())
|
||||
video_modes[i].type |= MODE_DELETE;
|
||||
|
||||
// Now restore all modes which timings have been modified
|
||||
else if (modeline_is_different(&video_modes[i], &backup_modes[i]))
|
||||
{
|
||||
video_modes[i] = backup_modes[i];
|
||||
video_modes[i].type |= MODE_UPDATE;
|
||||
}
|
||||
}
|
||||
// Finally, flush pending changes to driver
|
||||
return flush_modes();
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::flush_modes
|
||||
//============================================================
|
||||
|
||||
bool display_manager::flush_modes()
|
||||
{
|
||||
bool error = false;
|
||||
std::vector<modeline *> modified_modes = {};
|
||||
|
||||
if (video() == nullptr)
|
||||
return false;
|
||||
|
||||
// Loop through our mode table to collect all pending changes
|
||||
for (auto &mode : video_modes)
|
||||
if (mode.type & (MODE_UPDATE | MODE_ADD | MODE_DELETE))
|
||||
modified_modes.push_back(&mode);
|
||||
|
||||
// Flush pending changes to driver
|
||||
if (modified_modes.size() > 0)
|
||||
{
|
||||
video()->process_modelist(modified_modes);
|
||||
|
||||
// Log error/success result for each mode
|
||||
for (auto &mode : modified_modes)
|
||||
{
|
||||
log_verbose("Switchres: %s %s mode ", mode->type & MODE_ERROR? "error" : "success", mode->type & MODE_DELETE? "deleting" : mode->type & MODE_ADD? "adding" : "updating");
|
||||
log_mode(mode);
|
||||
|
||||
if (mode->type & MODE_ERROR)
|
||||
error = true;
|
||||
}
|
||||
|
||||
// Update our internal mode table to reflect the changes
|
||||
for (unsigned i = video_modes.size(); i-- > 0; )
|
||||
{
|
||||
if (video_modes[i].type & MODE_ERROR)
|
||||
continue;
|
||||
|
||||
if (video_modes[i].type & MODE_DELETE)
|
||||
{
|
||||
video_modes.erase(video_modes.begin() + i);
|
||||
m_best_mode = 0;
|
||||
}
|
||||
else
|
||||
video_modes[i].type &= ~(MODE_UPDATE | MODE_ADD);
|
||||
}
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::filter_modes
|
||||
//============================================================
|
||||
|
||||
bool display_manager::filter_modes()
|
||||
{
|
||||
for (auto &mode : video_modes)
|
||||
{
|
||||
// apply options to mode type
|
||||
if (m_ds.refresh_dont_care)
|
||||
mode.type |= V_FREQ_EDITABLE;
|
||||
|
||||
if ((caps() & CUSTOM_VIDEO_CAPS_UPDATE))
|
||||
mode.type |= V_FREQ_EDITABLE;
|
||||
|
||||
if (caps() & CUSTOM_VIDEO_CAPS_SCAN_EDITABLE)
|
||||
mode.type |= SCAN_EDITABLE;
|
||||
|
||||
if (!m_ds.modeline_generation)
|
||||
mode.type &= ~(XYV_EDITABLE | SCAN_EDITABLE);
|
||||
|
||||
if ((mode.type & MODE_DESKTOP) && !(caps() & CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE))
|
||||
mode.type &= ~V_FREQ_EDITABLE;
|
||||
|
||||
if (m_ds.lock_system_modes && (mode.type & CUSTOM_VIDEO_TIMING_SYSTEM))
|
||||
mode.type |= MODE_DISABLED;
|
||||
|
||||
// Make sure to unlock the desktop mode as fallback
|
||||
if (mode.type & MODE_DESKTOP)
|
||||
mode.type &= ~MODE_DISABLED;
|
||||
|
||||
// Lock all modes that don't match the user's -resolution rules
|
||||
if (m_user_mode.width != 0 || m_user_mode.height != 0 || m_user_mode.refresh == !0)
|
||||
{
|
||||
if (!( (mode.width == m_user_mode.width || (mode.type & X_RES_EDITABLE) || m_user_mode.width == 0)
|
||||
&& (mode.height == m_user_mode.height || (mode.type & Y_RES_EDITABLE) || m_user_mode.height == 0)
|
||||
&& (mode.refresh == m_user_mode.refresh || (mode.type & V_FREQ_EDITABLE) || m_user_mode.refresh == 0) ))
|
||||
mode.type |= MODE_DISABLED;
|
||||
else
|
||||
mode.type &= ~MODE_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::get_video_mode
|
||||
//============================================================
|
||||
|
||||
modeline *display_manager::get_mode(int width, int height, float refresh, bool interlaced)
|
||||
{
|
||||
modeline s_mode = {};
|
||||
modeline t_mode = {};
|
||||
modeline best_mode = {};
|
||||
char result[256]={'\x00'};
|
||||
|
||||
log_verbose("Switchres: Calculating best video mode for %dx%d@%.6f%s orientation: %s\n",
|
||||
width, height, refresh, interlaced?"i":"", rotation()?"rotated":"normal");
|
||||
|
||||
best_mode.result.weight |= R_OUT_OF_RANGE;
|
||||
|
||||
s_mode.interlace = interlaced;
|
||||
s_mode.vfreq = refresh;
|
||||
|
||||
s_mode.hactive = normalize(width, 8);
|
||||
s_mode.vactive = height;
|
||||
|
||||
if (rotation()) std::swap(s_mode.hactive, s_mode.vactive);
|
||||
|
||||
// Create a dummy mode entry if allowed
|
||||
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation)
|
||||
{
|
||||
modeline new_mode = {};
|
||||
new_mode.type = XYV_EDITABLE | V_FREQ_EDITABLE | SCAN_EDITABLE | MODE_ADD | (desktop_is_rotated()? MODE_ROTATED : MODE_OK);
|
||||
video_modes.push_back(new_mode);
|
||||
}
|
||||
|
||||
// Run through our mode list and find the most suitable mode
|
||||
for (auto &mode : video_modes)
|
||||
{
|
||||
log_verbose("\nSwitchres: %s%4d%sx%s%4d%s_%s%d=%.6fHz%s%s\n",
|
||||
mode.type & X_RES_EDITABLE?"(":"[", mode.width, mode.type & X_RES_EDITABLE?")":"]",
|
||||
mode.type & Y_RES_EDITABLE?"(":"[", mode.height, mode.type & Y_RES_EDITABLE?")":"]",
|
||||
mode.type & V_FREQ_EDITABLE?"(":"[", mode.refresh, mode.vfreq, mode.type & V_FREQ_EDITABLE?")":"]",
|
||||
mode.type & MODE_DISABLED?" - locked":"");
|
||||
|
||||
// now get the mode if allowed
|
||||
if (!(mode.type & MODE_DISABLED))
|
||||
{
|
||||
for (int i = 0 ; i < MAX_RANGES ; i++)
|
||||
{
|
||||
if (range[i].hfreq_min)
|
||||
{
|
||||
t_mode = mode;
|
||||
|
||||
// init all editable fields with source or user values
|
||||
if (t_mode.type & X_RES_EDITABLE)
|
||||
t_mode.hactive = m_user_mode.width? m_user_mode.width : s_mode.hactive;
|
||||
|
||||
if (t_mode.type & Y_RES_EDITABLE)
|
||||
t_mode.vactive = m_user_mode.height? m_user_mode.height : s_mode.vactive;
|
||||
|
||||
if (t_mode.type & V_FREQ_EDITABLE)
|
||||
{
|
||||
// If user's vfreq is defined, it means we have an user modeline, so force it
|
||||
if (m_user_mode.vfreq)
|
||||
t_mode = m_user_mode;
|
||||
else
|
||||
t_mode.vfreq = s_mode.vfreq;
|
||||
}
|
||||
|
||||
// lock resolution fields if required
|
||||
if (m_user_mode.width) t_mode.type &= ~X_RES_EDITABLE;
|
||||
if (m_user_mode.height) t_mode.type &= ~Y_RES_EDITABLE;
|
||||
if (m_user_mode.vfreq) t_mode.type &= ~V_FREQ_EDITABLE;
|
||||
|
||||
modeline_create(&s_mode, &t_mode, &range[i], &m_ds.gs);
|
||||
t_mode.range = i;
|
||||
|
||||
log_verbose("%s\n", modeline_result(&t_mode, result));
|
||||
|
||||
if (modeline_compare(&t_mode, &best_mode))
|
||||
{
|
||||
best_mode = t_mode;
|
||||
m_best_mode = &mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't need to create a new mode, remove our dummy entry
|
||||
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation && m_best_mode != &video_modes.back())
|
||||
video_modes.pop_back();
|
||||
|
||||
// If we didn't find a suitable mode, exit now
|
||||
if (best_mode.result.weight & R_OUT_OF_RANGE)
|
||||
{
|
||||
m_best_mode = 0;
|
||||
log_error("Switchres: could not find a video mode that meets your specs\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
log_verbose("\nSwitchres: %s (%dx%d@%.6f)->(%dx%d@%.6f)\n", rotation()?"rotated":"normal",
|
||||
width, height, refresh, best_mode.hactive, best_mode.vactive, best_mode.vfreq);
|
||||
|
||||
log_verbose("%s\n", modeline_result(&best_mode, result));
|
||||
|
||||
// Copy the new modeline to our mode list
|
||||
if (m_ds.modeline_generation)
|
||||
{
|
||||
if (best_mode.type & MODE_ADD)
|
||||
{
|
||||
best_mode.width = best_mode.hactive;
|
||||
best_mode.height = best_mode.vactive;
|
||||
best_mode.refresh = int(best_mode.vfreq);
|
||||
// lock new mode
|
||||
best_mode.type &= ~(X_RES_EDITABLE | Y_RES_EDITABLE | (caps() & CUSTOM_VIDEO_CAPS_UPDATE? 0 : V_FREQ_EDITABLE));
|
||||
}
|
||||
else if (modeline_is_different(&best_mode, m_best_mode) != 0)
|
||||
best_mode.type |= MODE_UPDATE;
|
||||
|
||||
char modeline[256]={'\x00'};
|
||||
log_info("Switchres: Modeline %s\n", modeline_print(&best_mode, modeline, MS_FULL));
|
||||
}
|
||||
|
||||
// Check if new best mode is different than previous one
|
||||
m_switching_required = (m_current_mode != m_best_mode || best_mode.type & MODE_UPDATE);
|
||||
|
||||
*m_best_mode = best_mode;
|
||||
return m_best_mode;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// display_manager::auto_specs
|
||||
//============================================================
|
||||
|
||||
bool display_manager::auto_specs()
|
||||
{
|
||||
// Make sure we have a valid mode
|
||||
if (desktop_mode.width == 0 || desktop_mode.height == 0 || desktop_mode.refresh == 0)
|
||||
{
|
||||
log_error("Switchres: Invalid desktop mode %dx%d@%d\n", desktop_mode.width, desktop_mode.height, desktop_mode.refresh);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_verbose("Switchres: Creating automatic specs for LCD based on %s\n", (desktop_mode.type & CUSTOM_VIDEO_TIMING_SYSTEM)? "VESA GTF" : "current timings");
|
||||
|
||||
// Make sure our current refresh is within range if set to auto
|
||||
if (!strcmp(m_ds.lcd_range, "auto"))
|
||||
{
|
||||
sprintf(m_ds.lcd_range, "%d-%d", desktop_mode.refresh - 1, desktop_mode.refresh + 1);
|
||||
monitor_fill_lcd_range(range, m_ds.lcd_range);
|
||||
}
|
||||
|
||||
// Create a working range with the best possible information
|
||||
if (desktop_mode.type & CUSTOM_VIDEO_TIMING_SYSTEM) modeline_vesa_gtf(&desktop_mode);
|
||||
modeline_to_monitor_range(range, &desktop_mode);
|
||||
monitor_show_range(range);
|
||||
|
||||
// Force our resolution to LCD's native one
|
||||
modeline user_mode = {};
|
||||
user_mode.width = desktop_mode.width;
|
||||
user_mode.height = desktop_mode.height;
|
||||
user_mode.refresh = desktop_mode.refresh;
|
||||
set_user_mode(&user_mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
Vendored
+162
@@ -0,0 +1,162 @@
|
||||
/**************************************************************
|
||||
|
||||
display.h - Display manager
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#ifndef __DISPLAY_H__
|
||||
#define __DISPLAY_H__
|
||||
|
||||
#include <vector>
|
||||
#include "modeline.h"
|
||||
#include "custom_video.h"
|
||||
|
||||
typedef struct display_settings
|
||||
{
|
||||
char screen[32];
|
||||
char api[32];
|
||||
bool modeline_generation;
|
||||
bool lock_unsupported_modes;
|
||||
bool lock_system_modes;
|
||||
bool refresh_dont_care;
|
||||
bool keep_changes;
|
||||
char monitor[32];
|
||||
char crt_range[MAX_RANGES][256];
|
||||
char lcd_range[256];
|
||||
char user_modeline[256];
|
||||
modeline user_mode;
|
||||
|
||||
generator_settings gs;
|
||||
custom_video_settings vs;
|
||||
} display_settings;
|
||||
|
||||
|
||||
class display_manager
|
||||
{
|
||||
public:
|
||||
|
||||
display_manager() {};
|
||||
virtual ~display_manager()
|
||||
{
|
||||
if (!m_ds.keep_changes) restore_modes();
|
||||
if (m_factory) delete m_factory;
|
||||
};
|
||||
|
||||
display_manager *make(display_settings *ds);
|
||||
void parse_options();
|
||||
virtual bool init();
|
||||
virtual int caps();
|
||||
|
||||
// getters
|
||||
custom_video *factory() const { return m_factory; }
|
||||
custom_video *video() const { return m_video; }
|
||||
modeline user_mode() const { return m_user_mode; }
|
||||
modeline *best_mode() const { return m_best_mode; }
|
||||
modeline *current_mode() const { return m_current_mode; }
|
||||
int index() const { return m_index; }
|
||||
bool desktop_is_rotated() const { return m_desktop_is_rotated; }
|
||||
|
||||
// getters (display manager)
|
||||
const char *set_monitor() { return (const char*) &m_ds.monitor; }
|
||||
const char *user_modeline() { return (const char*) &m_ds.user_modeline; }
|
||||
const char *crt_range(int i) { return (const char*) &m_ds.crt_range[i]; }
|
||||
const char *lcd_range() { return (const char*) &m_ds.lcd_range; }
|
||||
const char *screen() { return (const char*) &m_ds.screen; }
|
||||
const char *api() { return (const char*) &m_ds.api; }
|
||||
bool modeline_generation() { return m_ds.modeline_generation; }
|
||||
bool lock_unsupported_modes() { return m_ds.lock_unsupported_modes; }
|
||||
bool lock_system_modes() { return m_ds.lock_system_modes; }
|
||||
bool refresh_dont_care() { return m_ds.refresh_dont_care; }
|
||||
bool keep_changes() { return m_ds.keep_changes; }
|
||||
|
||||
// getters (modeline generator)
|
||||
bool interlace() { return m_ds.gs.interlace; }
|
||||
bool doublescan() { return m_ds.gs.doublescan; }
|
||||
double dotclock_min() { return m_ds.gs.pclock_min; }
|
||||
double refresh_tolerance() { return m_ds.gs.refresh_tolerance; }
|
||||
int super_width() { return m_ds.gs.super_width; }
|
||||
bool rotation() { return m_ds.gs.rotation; }
|
||||
double monitor_aspect() { return m_ds.gs.monitor_aspect; }
|
||||
int v_shift_correct() { return m_ds.gs.v_shift_correct; }
|
||||
int pixel_precision() { return m_ds.gs.pixel_precision; }
|
||||
int interlace_force_even() { return m_ds.gs.interlace_force_even; }
|
||||
|
||||
// getters (modeline result)
|
||||
bool got_mode() { return (m_best_mode != nullptr); }
|
||||
int width() { return m_best_mode != nullptr? m_best_mode->width : 0; }
|
||||
int height() { return m_best_mode != nullptr? m_best_mode->height : 0; }
|
||||
int refresh() { return m_best_mode != nullptr? m_best_mode->refresh : 0; }
|
||||
double v_freq() { return m_best_mode != nullptr? m_best_mode->vfreq : 0; }
|
||||
double h_freq() { return m_best_mode != nullptr? m_best_mode->hfreq : 0; }
|
||||
int x_scale() { return m_best_mode != nullptr? m_best_mode->result.x_scale : 0; }
|
||||
int y_scale() { return m_best_mode != nullptr? m_best_mode->result.y_scale : 0; }
|
||||
int v_scale() { return m_best_mode != nullptr? m_best_mode->result.v_scale : 0; }
|
||||
bool is_interlaced() { return m_best_mode != nullptr? m_best_mode->interlace : false; }
|
||||
bool is_doublescanned() { return m_best_mode != nullptr? m_best_mode->doublescan : false; }
|
||||
bool is_stretched() { return m_best_mode != nullptr? m_best_mode->result.weight & R_RES_STRETCH : false; }
|
||||
bool is_refresh_off() { return m_best_mode != nullptr? m_best_mode->result.weight & R_V_FREQ_OFF : false; }
|
||||
bool is_switching_required() { return m_switching_required; }
|
||||
bool is_mode_updated() { return m_best_mode != nullptr? m_best_mode->type & MODE_UPDATE : false; }
|
||||
bool is_mode_new() { return m_best_mode != nullptr? m_best_mode->type & MODE_ADD : false; }
|
||||
|
||||
// setters
|
||||
void set_factory(custom_video *factory) { m_factory = factory; }
|
||||
void set_custom_video(custom_video *video) { m_video = video; }
|
||||
void set_user_mode(modeline *mode) { m_user_mode = *mode; filter_modes(); }
|
||||
void set_current_mode(modeline *mode) { m_current_mode = mode; }
|
||||
void set_index(int index) { m_index = index; }
|
||||
void set_desktop_is_rotated(bool value) { m_desktop_is_rotated = value; }
|
||||
void set_rotation(bool value) { m_ds.gs.rotation = value; }
|
||||
void set_monitor_aspect(float aspect) { m_ds.gs.monitor_aspect = aspect; }
|
||||
void set_v_shift_correct(int value) { m_ds.gs.v_shift_correct = value; }
|
||||
void set_pixel_precision(int value) { m_ds.gs.pixel_precision = value; }
|
||||
|
||||
// options
|
||||
display_settings m_ds = {};
|
||||
|
||||
// mode setting interface
|
||||
modeline *get_mode(int width, int height, float refresh, bool interlaced);
|
||||
bool add_mode(modeline *mode);
|
||||
bool delete_mode(modeline *mode);
|
||||
bool update_mode(modeline *mode);
|
||||
virtual bool set_mode(modeline *);
|
||||
void log_mode(modeline *mode);
|
||||
|
||||
// mode list handling
|
||||
bool filter_modes();
|
||||
bool restore_modes();
|
||||
bool flush_modes();
|
||||
bool auto_specs();
|
||||
|
||||
// mode list
|
||||
std::vector<modeline> video_modes = {};
|
||||
std::vector<modeline> backup_modes = {};
|
||||
modeline desktop_mode = {};
|
||||
|
||||
// monitor preset
|
||||
monitor_range range[MAX_RANGES];
|
||||
|
||||
private:
|
||||
|
||||
// custom video backend
|
||||
custom_video *m_factory = 0;
|
||||
custom_video *m_video = 0;
|
||||
|
||||
modeline m_user_mode = {};
|
||||
modeline *m_best_mode = 0;
|
||||
modeline *m_current_mode = 0;
|
||||
|
||||
int m_index = 0;
|
||||
bool m_desktop_is_rotated = 0;
|
||||
bool m_switching_required = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+168
@@ -0,0 +1,168 @@
|
||||
/**************************************************************
|
||||
|
||||
display_linux.cpp - Display manager for Linux
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "display_linux.h"
|
||||
#include "log.h"
|
||||
|
||||
//============================================================
|
||||
// linux_display::linux_display
|
||||
//============================================================
|
||||
|
||||
linux_display::linux_display(display_settings *ds)
|
||||
{
|
||||
// Get display settings
|
||||
m_ds = *ds;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::~linux_display
|
||||
//============================================================
|
||||
|
||||
linux_display::~linux_display()
|
||||
{
|
||||
if (!m_ds.keep_changes)
|
||||
restore_desktop_mode();
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::init
|
||||
//============================================================
|
||||
|
||||
bool linux_display::init()
|
||||
{
|
||||
// Initialize custom video
|
||||
int method = CUSTOM_VIDEO_TIMING_AUTO;
|
||||
|
||||
#ifdef SR_WITH_XRANDR
|
||||
if (!strcmp(m_ds.api, "xrandr"))
|
||||
method = CUSTOM_VIDEO_TIMING_XRANDR;
|
||||
#endif
|
||||
#ifdef SR_WITH_KMSDRM
|
||||
if (!strcmp(m_ds.api, "drmkms"))
|
||||
method = CUSTOM_VIDEO_TIMING_DRMKMS;
|
||||
#endif
|
||||
|
||||
set_factory(new custom_video);
|
||||
set_custom_video(factory()->make(m_ds.screen, NULL, method, &m_ds.vs));
|
||||
if (!video() or !video()->init())
|
||||
return false;
|
||||
|
||||
// Build our display's mode list
|
||||
video_modes.clear();
|
||||
backup_modes.clear();
|
||||
get_desktop_mode();
|
||||
get_available_video_modes();
|
||||
|
||||
if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
|
||||
filter_modes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::set_mode
|
||||
//============================================================
|
||||
|
||||
bool linux_display::set_mode(modeline *mode)
|
||||
{
|
||||
if (mode && set_desktop_mode(mode, 0))
|
||||
{
|
||||
set_current_mode(mode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::get_desktop_mode
|
||||
//============================================================
|
||||
|
||||
bool linux_display::get_desktop_mode()
|
||||
{
|
||||
if (video() == NULL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::set_desktop_mode
|
||||
//============================================================
|
||||
|
||||
bool linux_display::set_desktop_mode(modeline *mode, int flags)
|
||||
{
|
||||
if (!mode)
|
||||
return false;
|
||||
|
||||
if (video() == NULL)
|
||||
return false;
|
||||
|
||||
if (flags != 0)
|
||||
log_info("Set desktop mode flags value is 0x%x.\n", flags);
|
||||
|
||||
return video()->set_timing(mode);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::restore_desktop_mode
|
||||
//============================================================
|
||||
|
||||
bool linux_display::restore_desktop_mode()
|
||||
{
|
||||
if (video() == NULL)
|
||||
return false;
|
||||
|
||||
return video()->set_timing(&desktop_mode);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// linux_display::get_available_video_modes
|
||||
//============================================================
|
||||
|
||||
int linux_display::get_available_video_modes()
|
||||
{
|
||||
if (video() == NULL)
|
||||
return false;
|
||||
|
||||
// loop through all modes until NULL mode type is received
|
||||
for (;;)
|
||||
{
|
||||
modeline mode;
|
||||
memset(&mode, 0, sizeof(struct modeline));
|
||||
|
||||
// get next mode
|
||||
video()->get_timing(&mode);
|
||||
if (mode.type == 0 || mode.platform_data == 0)
|
||||
break;
|
||||
|
||||
// set the desktop mode
|
||||
if (mode.type & MODE_DESKTOP)
|
||||
{
|
||||
memcpy(&desktop_mode, &mode, sizeof(modeline));
|
||||
if (current_mode() == nullptr)
|
||||
set_current_mode(&mode);
|
||||
}
|
||||
|
||||
video_modes.push_back(mode);
|
||||
backup_modes.push_back(mode);
|
||||
|
||||
log_verbose("Switchres: [%3ld] %4dx%4d @%3d%s%s %s: ", video_modes.size(), mode.width, mode.height, mode.refresh, mode.interlace ? "i" : "p", mode.type & MODE_DESKTOP ? "*" : "", mode.type & MODE_ROTATED ? "rot" : "");
|
||||
log_mode(&mode);
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
/**************************************************************
|
||||
|
||||
display_linux.h - Display manager for Linux
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include "display.h"
|
||||
|
||||
class linux_display : public display_manager
|
||||
{
|
||||
public:
|
||||
linux_display(display_settings *ds);
|
||||
~linux_display();
|
||||
bool init();
|
||||
bool set_mode(modeline *mode);
|
||||
|
||||
private:
|
||||
bool get_desktop_mode();
|
||||
bool set_desktop_mode(modeline *mode, int flags);
|
||||
bool restore_desktop_mode();
|
||||
int get_available_video_modes();
|
||||
};
|
||||
Vendored
+260
@@ -0,0 +1,260 @@
|
||||
/**************************************************************
|
||||
|
||||
display_windows.cpp - Display manager for Windows
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "display_windows.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
//============================================================
|
||||
// windows_display::windows_display
|
||||
//============================================================
|
||||
|
||||
windows_display::windows_display(display_settings *ds)
|
||||
{
|
||||
// Get display settings
|
||||
m_ds = *ds;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::~windows_display
|
||||
//============================================================
|
||||
|
||||
windows_display::~windows_display()
|
||||
{
|
||||
// Restore previous settings
|
||||
if (!m_ds.keep_changes) ChangeDisplaySettingsExA(m_device_name, NULL, NULL, 0, 0);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::init
|
||||
//============================================================
|
||||
|
||||
bool windows_display::init()
|
||||
{
|
||||
DISPLAY_DEVICEA lpDisplayDevice[DISPLAY_MAX];
|
||||
int idev = 0;
|
||||
int found = -1;
|
||||
|
||||
while (idev < DISPLAY_MAX)
|
||||
{
|
||||
memset(&lpDisplayDevice[idev], 0, sizeof(DISPLAY_DEVICEA));
|
||||
lpDisplayDevice[idev].cb = sizeof(DISPLAY_DEVICEA);
|
||||
|
||||
if (EnumDisplayDevicesA(NULL, idev, &lpDisplayDevice[idev], 0) == FALSE)
|
||||
break;
|
||||
|
||||
if ((!strcmp(m_ds.screen, "auto") && (lpDisplayDevice[idev].StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
|
||||
|| !strcmp(m_ds.screen, lpDisplayDevice[idev].DeviceName) || m_ds.screen[0] - '0' == idev)
|
||||
found = idev;
|
||||
|
||||
idev++;
|
||||
}
|
||||
if (found != -1)
|
||||
{
|
||||
strncpy(m_device_name, lpDisplayDevice[found].DeviceName, sizeof(m_device_name) -1);
|
||||
strncpy(m_device_id, lpDisplayDevice[found].DeviceID, sizeof(m_device_id) -1);
|
||||
log_verbose("Switchres: %s: %s (%s)\n", m_device_name, lpDisplayDevice[found].DeviceString, m_device_id);
|
||||
|
||||
char *pch;
|
||||
int i;
|
||||
for (i = 0; i < idev; i++)
|
||||
{
|
||||
pch = strstr(lpDisplayDevice[i].DeviceString, lpDisplayDevice[found].DeviceString);
|
||||
if (pch)
|
||||
{
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *chsrc, *chdst;
|
||||
chdst = m_device_key;
|
||||
|
||||
for (chsrc = lpDisplayDevice[i].DeviceKey + 18; *chsrc != 0; chsrc++)
|
||||
*chdst++ = *chsrc;
|
||||
|
||||
*chdst = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Switchres: Failed obtaining default video registry key\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_verbose("Switchres: Device key: %s\n", m_device_key);
|
||||
|
||||
// Initialize custom video
|
||||
int method = CUSTOM_VIDEO_TIMING_AUTO;
|
||||
if(!strcmp(m_ds.api, "powerstrip")) method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
|
||||
strcpy(m_ds.vs.device_reg_key, m_device_key);
|
||||
|
||||
// Create custom video backend
|
||||
set_factory(new custom_video);
|
||||
set_custom_video(factory()->make(m_device_name, m_device_id, method, &m_ds.vs));
|
||||
if (video()) video()->init();
|
||||
|
||||
// Build our display's mode list
|
||||
video_modes.clear();
|
||||
backup_modes.clear();
|
||||
get_desktop_mode();
|
||||
get_available_video_modes();
|
||||
if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
|
||||
filter_modes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::set_mode
|
||||
//============================================================
|
||||
|
||||
bool windows_display::set_mode(modeline *mode)
|
||||
{
|
||||
if (mode && set_desktop_mode(mode, (m_ds.keep_changes? CDS_UPDATEREGISTRY : CDS_FULLSCREEN) | CDS_RESET))
|
||||
{
|
||||
set_current_mode(mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::get_desktop_mode
|
||||
//============================================================
|
||||
|
||||
bool windows_display::get_desktop_mode()
|
||||
{
|
||||
memset(&m_devmode, 0, sizeof(DEVMODEA));
|
||||
m_devmode.dmSize = sizeof(DEVMODEA);
|
||||
|
||||
if (EnumDisplaySettingsExA(!strcmp(m_device_name, "auto")?NULL:m_device_name, ENUM_CURRENT_SETTINGS, &m_devmode, 0))
|
||||
{
|
||||
desktop_mode.width = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsWidth:m_devmode.dmPelsHeight;
|
||||
desktop_mode.height = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsHeight:m_devmode.dmPelsWidth;
|
||||
desktop_mode.refresh = m_devmode.dmDisplayFrequency;
|
||||
desktop_mode.interlace = (m_devmode.dmDisplayFlags & DM_INTERLACED)?1:0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::set_desktop_mode
|
||||
//============================================================
|
||||
|
||||
bool windows_display::set_desktop_mode(modeline *mode, int flags)
|
||||
{
|
||||
if (mode)
|
||||
{
|
||||
DEVMODEA lpDevMode;
|
||||
memset(&lpDevMode, 0, sizeof(DEVMODEA));
|
||||
lpDevMode.dmSize = sizeof(DEVMODEA);
|
||||
lpDevMode.dmPelsWidth = mode->type & MODE_ROTATED? mode->height : mode->width;
|
||||
lpDevMode.dmPelsHeight = mode->type & MODE_ROTATED? mode->width : mode->height;
|
||||
lpDevMode.dmDisplayFrequency = (int)mode->refresh;
|
||||
lpDevMode.dmDisplayFlags = mode->interlace? DM_INTERLACED : 0;
|
||||
lpDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
|
||||
|
||||
log_info("set_desktop_mode: %s (%dx%d@%d) flags(%x)\n", m_device_name, (int)lpDevMode.dmPelsWidth, (int)lpDevMode.dmPelsHeight, (int)lpDevMode.dmDisplayFrequency, (int)lpDevMode.dmDisplayFlags);
|
||||
|
||||
int result = ChangeDisplaySettingsExA(m_device_name, &lpDevMode, NULL, flags, 0);
|
||||
if (result == DISP_CHANGE_SUCCESSFUL)
|
||||
return true;
|
||||
|
||||
log_error("ChangeDisplaySettingsExA error(%x)\n", (int)result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::restore_desktop_mode
|
||||
//============================================================
|
||||
|
||||
bool windows_display::restore_desktop_mode()
|
||||
{
|
||||
if (ChangeDisplaySettingsExA(m_device_name, &m_devmode, NULL, 0, 0) == DISP_CHANGE_SUCCESSFUL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// windows_display::get_available_video_modes
|
||||
//============================================================
|
||||
|
||||
int windows_display::get_available_video_modes()
|
||||
{
|
||||
int iModeNum = 0, j = 0, k = 0;
|
||||
DEVMODEA lpDevMode;
|
||||
|
||||
memset(&lpDevMode, 0, sizeof(DEVMODEA));
|
||||
lpDevMode.dmSize = sizeof(DEVMODEA);
|
||||
|
||||
log_verbose("Switchres: Searching for custom video modes...\n");
|
||||
|
||||
while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, m_ds.lock_unsupported_modes?0:EDS_RAWMODE) != 0)
|
||||
{
|
||||
if (lpDevMode.dmBitsPerPel == 32 && lpDevMode.dmDisplayFixedOutput == DMDFO_DEFAULT)
|
||||
{
|
||||
modeline m;
|
||||
memset(&m, 0, sizeof(struct modeline));
|
||||
m.interlace = (lpDevMode.dmDisplayFlags & DM_INTERLACED)?1:0;
|
||||
m.width = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsWidth:lpDevMode.dmPelsHeight;
|
||||
m.height = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsHeight:lpDevMode.dmPelsWidth;
|
||||
m.refresh = lpDevMode.dmDisplayFrequency;
|
||||
m.hactive = m.width;
|
||||
m.vactive = m.height;
|
||||
m.vfreq = m.refresh;
|
||||
m.type |= lpDevMode.dmDisplayOrientation == DMDO_90 || lpDevMode.dmDisplayOrientation == DMDO_270? MODE_ROTATED : MODE_OK;
|
||||
|
||||
for (auto &mode : video_modes) if (mode.width == m.width && mode.height == m.height && mode.refresh == m.refresh && m.interlace == mode.interlace) goto found;
|
||||
|
||||
if (m.width == desktop_mode.width && m.height == desktop_mode.height && m.refresh == desktop_mode.refresh && m.interlace == desktop_mode.interlace)
|
||||
{
|
||||
m.type |= MODE_DESKTOP;
|
||||
if (m.type & MODE_ROTATED) set_desktop_is_rotated(true);
|
||||
if (current_mode() == nullptr)
|
||||
set_current_mode(&m);
|
||||
}
|
||||
|
||||
log_verbose("Switchres: [%3d] %4dx%4d @%3d%s%s %s: ", k, m.width, m.height, m.refresh, m.interlace?"i":"p", m.type & MODE_DESKTOP?"*":"", m.type & MODE_ROTATED?"rot":"");
|
||||
|
||||
if (video() && video()->get_timing(&m))
|
||||
{
|
||||
j++;
|
||||
log_mode(&m);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.type |= CUSTOM_VIDEO_TIMING_SYSTEM;
|
||||
log_verbose("system mode\n");
|
||||
}
|
||||
|
||||
// Save our desktop mode now that we queried detailed timings
|
||||
if (m.type & MODE_DESKTOP) desktop_mode = m;
|
||||
|
||||
video_modes.push_back(m);
|
||||
backup_modes.push_back(m);
|
||||
k++;
|
||||
}
|
||||
found:
|
||||
iModeNum++;
|
||||
}
|
||||
k--;
|
||||
log_verbose("Switchres: Found %d custom of %d active video modes\n", j, k);
|
||||
return k;
|
||||
}
|
||||
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
/**************************************************************
|
||||
|
||||
display_windows.h - Display manager for Windows
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <windows.h>
|
||||
#include "display.h"
|
||||
|
||||
//============================================================
|
||||
// PARAMETERS
|
||||
//============================================================
|
||||
|
||||
// display modes
|
||||
#define DM_INTERLACED 0x00000002
|
||||
#define DISPLAY_MAX 16
|
||||
|
||||
|
||||
class windows_display : public display_manager
|
||||
{
|
||||
public:
|
||||
windows_display(display_settings *ds);
|
||||
~windows_display();
|
||||
bool init();
|
||||
bool set_mode(modeline *mode);
|
||||
|
||||
private:
|
||||
bool get_desktop_mode();
|
||||
bool set_desktop_mode(modeline *mode, int flags);
|
||||
bool restore_desktop_mode();
|
||||
int get_available_video_modes();
|
||||
|
||||
char m_device_name[32];
|
||||
char m_device_id[128];
|
||||
char m_device_key[128];
|
||||
DEVMODEA m_devmode;
|
||||
};
|
||||
Vendored
+244
@@ -0,0 +1,244 @@
|
||||
/**************************************************************
|
||||
|
||||
edid.c - Basic EDID generation
|
||||
(based on edid.S: EDID data template by Carsten Emde)
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "switchres.h"
|
||||
#include "edid.h"
|
||||
|
||||
//============================================================
|
||||
// edid_from_modeline
|
||||
//============================================================
|
||||
|
||||
int edid_from_modeline(modeline *mode, monitor_range *range, char *name, edid_block *edid)
|
||||
{
|
||||
if (!edid) return 0;
|
||||
|
||||
// header
|
||||
edid->b[0] = 0x00;
|
||||
edid->b[1] = 0xff;
|
||||
edid->b[2] = 0xff;
|
||||
edid->b[3] = 0xff;
|
||||
edid->b[4] = 0xff;
|
||||
edid->b[5] = 0xff;
|
||||
edid->b[6] = 0xff;
|
||||
edid->b[7] = 0x00;
|
||||
|
||||
// Manufacturer ID = "SWR"
|
||||
edid->b[8] = 0x4e;
|
||||
edid->b[9] = 0xf2;
|
||||
|
||||
// Manufacturer product code
|
||||
edid->b[10] = 0x00;
|
||||
edid->b[11] = 0x00;
|
||||
|
||||
// Serial number
|
||||
edid->b[12] = 0x00;
|
||||
edid->b[13] = 0x00;
|
||||
edid->b[14] = 0x00;
|
||||
edid->b[15] = 0x00;
|
||||
|
||||
// Week of manufacture
|
||||
edid->b[16] = 5;
|
||||
|
||||
// Year of manufacture
|
||||
edid->b[17] = 2021 - 1990;
|
||||
|
||||
// EDID version and revision
|
||||
edid->b[18] = 1;
|
||||
edid->b[19] = 3;
|
||||
|
||||
// video params
|
||||
edid->b[20] = 0x6d;
|
||||
|
||||
// Maximum H & V size in cm
|
||||
edid->b[21] = 48;
|
||||
edid->b[22] = 36;
|
||||
|
||||
// Gamma
|
||||
edid->b[23] = 120;
|
||||
|
||||
// Display features
|
||||
edid->b[24] = 0x0A;
|
||||
|
||||
// Chromacity coordinates;
|
||||
edid->b[25] = 0x5e;
|
||||
edid->b[26] = 0xc0;
|
||||
edid->b[27] = 0xa4;
|
||||
edid->b[28] = 0x59;
|
||||
edid->b[29] = 0x4a;
|
||||
edid->b[30] = 0x98;
|
||||
edid->b[31] = 0x25;
|
||||
edid->b[32] = 0x20;
|
||||
edid->b[33] = 0x50;
|
||||
edid->b[34] = 0x54;
|
||||
|
||||
// Established timings
|
||||
edid->b[35] = 0x00;
|
||||
edid->b[36] = 0x00;
|
||||
edid->b[37] = 0x00;
|
||||
|
||||
// Standard timing information
|
||||
edid->b[38] = 0x01;
|
||||
edid->b[39] = 0x01;
|
||||
edid->b[40] = 0x01;
|
||||
edid->b[41] = 0x01;
|
||||
edid->b[42] = 0x01;
|
||||
edid->b[43] = 0x01;
|
||||
edid->b[44] = 0x01;
|
||||
edid->b[45] = 0x01;
|
||||
edid->b[46] = 0x01;
|
||||
edid->b[47] = 0x01;
|
||||
edid->b[48] = 0x01;
|
||||
edid->b[49] = 0x01;
|
||||
edid->b[50] = 0x01;
|
||||
edid->b[51] = 0x01;
|
||||
edid->b[52] = 0x01;
|
||||
edid->b[53] = 0x01;
|
||||
|
||||
// Pixel clock in 10 kHz units. (0.-655.35 MHz, little-endian)
|
||||
edid->b[54] = (mode->pclock / 10000) & 0xff;
|
||||
edid->b[55] = (mode->pclock / 10000) >> 8;
|
||||
|
||||
int h_active = mode->hactive;
|
||||
int h_blank = mode->htotal - mode->hactive;
|
||||
int h_offset = mode->hbegin - mode->hactive;
|
||||
int h_pulse = mode->hend - mode->hbegin;
|
||||
|
||||
int v_active = mode->vactive;
|
||||
int v_blank = (int)mode->vtotal - mode->vactive;
|
||||
int v_offset = mode->vbegin - mode->vactive;
|
||||
int v_pulse = mode->vend - mode->vbegin;
|
||||
|
||||
// Horizontal active pixels 8 lsbits (0-4095)
|
||||
edid->b[56] = h_active & 0xff;
|
||||
|
||||
// Horizontal blanking pixels 8 lsbits (0-4095)
|
||||
edid->b[57] = h_blank & 0xff;
|
||||
|
||||
// Bits 7-4 Horizontal active pixels 4 msbits
|
||||
// Bits 3-0 Horizontal blanking pixels 4 msbits
|
||||
edid->b[58] = (((h_active >> 8) & 0x0f) << 4) + ((h_blank >> 8) & 0x0f);
|
||||
|
||||
// Vertical active lines 8 lsbits (0-4095)
|
||||
edid->b[59] = v_active & 0xff;
|
||||
|
||||
// Vertical blanking lines 8 lsbits (0-4095)
|
||||
edid->b[60] = v_blank & 0xff;
|
||||
|
||||
// Bits 7-4 Vertical active lines 4 msbits
|
||||
// Bits 3-0 Vertical blanking lines 4 msbits
|
||||
edid->b[61] = (((v_active >> 8) & 0x0f) << 4) + ((v_blank >> 8) & 0x0f);
|
||||
|
||||
// Horizontal sync offset pixels 8 lsbits (0-1023) From blanking start
|
||||
edid->b[62] = h_offset & 0xff;
|
||||
|
||||
// Horizontal sync pulse width pixels 8 lsbits (0-1023)
|
||||
edid->b[63] = h_pulse & 0xff;
|
||||
|
||||
// Bits 7-4 Vertical sync offset lines 4 lsbits 0-63)
|
||||
// Bits 3-0 Vertical sync pulse width lines 4 lsbits 0-63)
|
||||
edid->b[64] = ((v_offset & 0x0f) << 4) + (v_pulse & 0x0f);
|
||||
|
||||
// Bits 7-6 Horizontal sync offset pixels 2 msbits
|
||||
// Bits 5-4 Horizontal sync pulse width pixels 2 msbits
|
||||
// Bits 3-2 Vertical sync offset lines 2 msbits
|
||||
// Bits 1-0 Vertical sync pulse width lines 2 msbits
|
||||
edid->b[65] = (((h_offset >> 8) & 0x03) << 6) +
|
||||
(((h_pulse >> 8) & 0x03) << 4) +
|
||||
(((v_offset >> 8) & 0x03) << 2) +
|
||||
((v_pulse >> 8) & 0x03);
|
||||
|
||||
// Horizontal display size, mm, 8 lsbits (0-4095 mm, 161 in)
|
||||
edid->b[66] = 485 & 0xff;
|
||||
|
||||
// Vertical display size, mm, 8 lsbits (0-4095 mm, 161 in)
|
||||
edid->b[67] = 364 & 0xff;
|
||||
|
||||
// Bits 7-4 Horizontal display size, mm, 4 msbits
|
||||
// Bits 3-0 Vertical display size, mm, 4 msbits
|
||||
edid->b[68] = (((485 >> 8) & 0x0f) << 4) + ((364 >> 8) & 0x0f);
|
||||
|
||||
// Horizontal border pixels (each side; total is twice this)
|
||||
edid->b[69] = 0;
|
||||
|
||||
// Vertical border lines (each side; total is twice this)
|
||||
edid->b[70] = 0;
|
||||
|
||||
// Features bitmap
|
||||
edid->b[71] = ((mode->interlace & 0x01) << 7) + 0x18 + (mode->vsync << 2) + (mode->hsync << 2);
|
||||
|
||||
|
||||
// Descriptor: monitor serial number
|
||||
edid->b[72] = 0;
|
||||
edid->b[73] = 0;
|
||||
edid->b[74] = 0;
|
||||
edid->b[75] = 0xff;
|
||||
edid->b[76] = 0;
|
||||
edid->b[77] = 'S';
|
||||
edid->b[78] = 'w';
|
||||
edid->b[79] = 'i';
|
||||
edid->b[80] = 't';
|
||||
edid->b[81] = 'c';
|
||||
edid->b[82] = 'h';
|
||||
edid->b[83] = 'r';
|
||||
edid->b[84] = 'e';
|
||||
edid->b[85] = 's';
|
||||
edid->b[86] = '2';
|
||||
edid->b[87] = '0';
|
||||
edid->b[88] = '0';
|
||||
edid->b[89] = 0x0a;
|
||||
|
||||
// Descriptor: monitor range limits
|
||||
edid->b[90] = 0;
|
||||
edid->b[91] = 0;
|
||||
edid->b[92] = 0;
|
||||
edid->b[93] = 0xfd;
|
||||
edid->b[94] = 0;
|
||||
edid->b[95] = ((int)range->vfreq_min) & 0xff;
|
||||
edid->b[96] = ((int)range->vfreq_max) & 0xff;
|
||||
edid->b[97] = ((int)range->hfreq_min / 1000) & 0xff;
|
||||
edid->b[98] = ((int)range->hfreq_max / 1000) & 0xff;
|
||||
edid->b[99] = 0xff;
|
||||
edid->b[100] = 0;
|
||||
edid->b[101] = 0x0a;
|
||||
edid->b[102] = 0x20;
|
||||
edid->b[103] = 0x20;
|
||||
edid->b[104] = 0x20;
|
||||
edid->b[105] = 0x20;
|
||||
edid->b[106] = 0x20;
|
||||
edid->b[107] = 0x20;
|
||||
|
||||
// Descriptor: text
|
||||
edid->b[108] = 0;
|
||||
edid->b[109] = 0;
|
||||
edid->b[110] = 0;
|
||||
edid->b[111] = 0xfc;
|
||||
edid->b[112] = 0;
|
||||
snprintf(&edid->b[113], 13, "%s", name);
|
||||
edid->b[125] = 0x0a;
|
||||
|
||||
// Extensions to follow
|
||||
edid->b[126] = 0;
|
||||
|
||||
// Compute checksum
|
||||
char checksum = 0;
|
||||
int i;
|
||||
for (i = 0; i <= 126; i++)
|
||||
checksum += edid->b[i];
|
||||
edid->b[127] = 256 - checksum;
|
||||
|
||||
return 1;
|
||||
}
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
/**************************************************************
|
||||
|
||||
edid.h - Basic EDID generation
|
||||
(based on edid.S: EDID data template by Carsten Emde)
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
Switchres Modeline generation engine for emulation
|
||||
|
||||
License GPL-2.0+
|
||||
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||||
Alexandre Wodarczyk, Gil Delescluse
|
||||
|
||||
**************************************************************/
|
||||
|
||||
#ifndef __EDID_H__
|
||||
#define __EDID_H__
|
||||
|
||||
//============================================================
|
||||
// TYPE DEFINITIONS
|
||||
//============================================================
|
||||
|
||||
typedef struct edid_block
|
||||
{
|
||||
char b[128];
|
||||
/* char ext1[128];
|
||||
char ext2[128];
|
||||
char ext3[128];*/
|
||||
} edid_block;
|
||||
|
||||
//============================================================
|
||||
// PROTOTYPES
|
||||
//============================================================
|
||||
|
||||
int edid_from_modeline(modeline *mode, monitor_range *range, char *name, edid_block *edid);
|
||||
|
||||
#endif
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
# ANY OS - BASIC INFORMATION
|
||||
|
||||
## Build libswitchres
|
||||
|
||||
It supports cross compilation, and will build both dynamic and static libs as per the target OS
|
||||
```bash
|
||||
make libswitchres
|
||||
```
|
||||
## Basic usage as a client with examples
|
||||
libswitchres can be called in 2 different ways (with example code):
|
||||
* `test_dlopen.c` -> by explicitely opening a .so/.dll, import the srlib object and call associated functions
|
||||
* `test_liblink.c` -> by simply linking libswitchres at build time
|
||||
|
||||
These options are generic whether you build for Linux or Windows
|
||||
* -I ../ (to get libswitchres_wrapper.h)
|
||||
* -L ../ or -L ./ (for win32, when the dll has been copied in the examples folder)
|
||||
* -lswitchres to link the lib if not manually opening it in the code
|
||||
|
||||
#please note#: static libs aven't been tested yet
|
||||
|
||||
# LINUX
|
||||
|
||||
You'll need a few extra parameters for gcc:
|
||||
* -ldl (will try later to find a way to statically link libdl.a)
|
||||
|
||||
When running, dont forget to add before the binary LD_LIBRARY_PATH=<libswitchres.so pass, even if it's ./>:$LD_LIBRARY_PATH
|
||||
|
||||
## Examples:
|
||||
```bash
|
||||
make libswitchres
|
||||
cd examples
|
||||
g++ -o linux_dl_test test_dlopen.cpp -I ../ -ldl
|
||||
LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH ./linux_dl_test
|
||||
|
||||
g++ -o linux_link_lib test_liblink.cpp -I ../ -L../ -lswitchres -ldl
|
||||
LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH ./linux_link_lib
|
||||
```
|
||||
|
||||
# WINDOWS
|
||||
|
||||
Pretty much the same as Linux, but with mingw64. The resulting exe and dll can be tested with wine
|
||||
|
||||
## Examples (cross-building from windows)
|
||||
|
||||
```
|
||||
make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- libswitchres
|
||||
(copy the dll to examples)
|
||||
|
||||
x86_64-w64-mingw32-g++-win32 test_dlopen.cpp -o w32_loaddll.exe -I ../ -static-libgcc -static-libstdc++
|
||||
w32_loaddll.exe
|
||||
|
||||
x86_64-w64-mingw32-g++-win32 test_liblink.cpp -o w32_linkdll.exe -I ../ -static-libgcc -static-libstdc++ -L ./ -lswitchres
|
||||
w32_linkdll.exe
|
||||
```
|
||||
|
||||
Note that, when building w32_linkdll.exe, I couldn't point to another dir else than ./ with -L
|
||||
Loaded 100 of 268 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user